Interweave Documentation

repository·master·Indexed 22 days ago

https://github.com/milesj/interweave

A robust React library for safely rendering and manipulating HTML content with built-in XSS and injection protection. It allows rendering HTML without dangerouslySetInnerHTML, stripping tags, and cleaning attributes. The ecosystem includes interweave-autolink for detecting and linking URLs, IPs, emails, hashtags, and mentions, as well as interweave-ssr for server-side rendering support via DOM polyfills. Requires React 16.8+ or 17+.

Tokens
26.4K
Snippets
85
Records
112
Agent score
75%

What's inside Interweave

  1. Interweave requirements and capabilities

    master

    Capabilities

    • Safely render HTML without dangerouslySetInnerHTML.
    • Strip HTML tags safely.
    • Automatic XSS and injection protection.
    • Clean HTML attributes using filters.
    • Interpolate components using matchers.
    • Autolink URLs, IPs, emails, and hashtags.
    • Render Emoji and emoticon characters.

    Requirements

    • React: 16.8+ or 17+
    • Browser Support: IE 11+
    • Emoji support: Requires fetch and sessionStorage
  2. Enable greedy matching in Matchers

    master

    By default, matchers run until they exhaust matches by shortening the string in each iteration. This can cause issues if a matcher uses multiple patterns that appear in unpredictable orders.

    To ensure a matcher always runs against the entire string in every iteration, set the greedy property to true.

    // For Class-based matchers
    class CustomMatcher extends Matcher<CustomProps> {
      greedy: boolean = true;
      // ...
    }
    
    // For Object-based matchers
    const matcher: MatcherInterface<CustomProps> = {
      greedy: true,
      // ...
    };
  3. How Interweave and Markup components work

    master

    Interweave provides two primary components for safely rendering HTML strings:

    1. <Interweave />: The full-featured component. It supports advanced features like filters, matchers, and lifecycle callbacks. Use this when you need to transform content, autolink URLs, or handle emojis.

    2. <Markup />: A lightweight component. It does not support matchers, filters, or callbacks. Use this when your only requirement is to safely render a string containing HTML without additional processing.

    import { Interweave, Markup } from 'interweave';
    
    // Full features (matchers, filters, etc)
    <Interweave content="<b>HTML</b>" />
    
    // Lightweight HTML rendering only
    <Markup content="<b>HTML</b>" />
  4. How Matchers work in Interweave

    master

    Matchers are the core mechanism in Interweave that allow you to insert React elements into strings using regex pattern matching.

    The Process:

    1. A matcher scans a string for a pattern.
    2. When a match is found, the string is deconstructed into tokens.
    3. The tokens are reconstructed into an array containing both plain strings and React elements.
    4. This array is then rendered by React's virtual DOM.

    Example Transformation: Input string: "Check out my website, github.com/milesj!" using a UrlMatcher results in: ['Check out my website, ', <Url>github.com/milesj</Url>, '!']

  5. Understand HTML rendering precedence

    master

    Interweave follows a specific hierarchy when deciding whether to render a tag. The order of precedence (from highest to lowest) is:

    1. Banned: Hard-coded tags (e.g., script, style, object) that are never rendered.
    2. Blocked: Tags explicitly provided by the consumer via the blockList prop.
    3. Allowed (via allowElements): If allowElements is true, all tags except Banned and Blocked tags are rendered. This has higher precedence than allowList.
    4. Allowed (via allowList): The default behavior where only tags in the allowList (or the default ALLOWED_TAG_LIST) are rendered.
  6. Create a custom filter

    master

    You can create a custom filter by either extending the Filter class or implementing the FilterInterface as a plain object.

    Filters can implement two types of methods:

    1. attribute(name: string, value: string): string: Receives the attribute name and its value. You must return the modified (or original) string value.
    2. node(name: string, node: HTMLElement): HTMLElement: Receives the tag name and the HTMLElement. You must return the modified HTMLElement.

    Important Rules:

    • You must return the attribute value or the node from the methods.
    • Returning null from the node method will remove that node from the DOM tree.
    import { Filter } from 'interweave';
    
    class LinkFilter extends Filter {
    	attribute(name: string, value: string): string {
    		if (name === 'href') {
    			return encodeURIComponent(value);
    		}
    
    		return value;
    	}
    
    	node(name: string, node: HTMLElement): HTMLElement {
    		if (name === 'a') {
    			node.setAttribute('target', '_blank');
    		}
    
    		return node;
    	}
    }
    
    const filter = new LinkFilter();
  7. Compose a custom Interweave component

    master

    Instead of using the default <Interweave /> component directly, it is recommended to compose a custom component around BaseInterweave from the interweave package. This approach allows you to centralize configuration for matchers (like URLs, emails, and emojis), filters, and specific behaviors such as hashtag URL formatting or emoji asset paths.

    By creating a wrapper, you can easily toggle between different platform behaviors (e.g., Twitter vs. Instagram hashtags) or different asset types (e.g., PNG vs. SVG emojis) across your entire application.

    import React from 'react';
    import { stripHexcode } from 'emojibase';
    import BaseInterweave, { InterweaveProps, FilterInterface, MatcherInterface } from 'interweave';
    import { IpMatcher, UrlMatcher, EmailMatcher, HashtagMatcher } from 'interweave-autolink';
    import { EmojiMatcher, PathConfig } from 'interweave-emoji';
    
    const globalFilters: FilterInterface[] = [new CustomFilter()];
    
    const globalMatchers: MatcherInterface[] = [
    	new EmailMatcher('email'),
    	new IpMatcher('ip'),
    	new UrlMatcher('url'),
    	new HashtagMatcher('hashtag'),
    	new EmojiMatcher('emoji', {
    		convertEmoticon: true,
    		convertShortcode: true,
    		convertUnicode: true,
    	}),
    ];
    
    function getEmojiPath(hexcode: string, { enlarged }: PathConfig): string {
    	return `//cdn.jsdelivr.net/emojione/assets/3.1/png/${enlarged ? 64 : 32}/${stripHexcode(
    		hexcode,
    	).toLowerCase()}.png`;
    }
    
    interface Props extends InterweaveProps {
    	instagram?: boolean;
    	twitter?: boolean;
    }
    
    export default function Interweave({
    	filters = [],
    	matchers = [],
    	twitter,
    	instagram,
    	...props
    }: Props) {
    	let hashtagUrl = '';
    
    	if (twitter) {
    		hashtagUrl = 'https://twitter.com/hashtag/{{hashtag}}';
    	} else if (instagram) {
    		hashtagUrl = 'https://instagram.com/explore/tags/{{hashtag}}';
    	}
    
    	return (
    		<BaseInterweave
    			filters={[...globalFilters, ...filters]}
    			matchers={[...globalMatchers, ...matchers]}
    			hashtagUrl={hashtagUrl}
    			emojiPath={getEmojiPath}
    			newWindow
    			{...props}
    		/>
    	);
    }
  8. Use and configure Matchers with <Interweave />

    master

    You can pass an array of matchers to the matchers prop of the <Interweave /> component. Each matcher must be instantiated with a unique camel-case name.

    Disabling Matchers

    • Disable all matchers: Pass the disableMatchers prop.
    • Disable a specific matcher: Pass a prop that starts with no followed by the unique name used in the matcher's constructor. For a matcher named 'foo', use the noFoo prop.
    // Adding matchers
    <Interweave matchers={[new CustomMatcher('foo')]} />
    
    // Disabling all matchers
    <Interweave disableMatchers />
    
    // Disabling a specific matcher named 'foo'
    <Interweave noFoo />
  9. Display emojis as SVGs or PNGs

    master

    To display images instead of unicode characters, set convertUnicode: true in the EmojiMatcher. You must then provide an emojiPath to the <Interweave /> component.

    Path Formats:

    1. String with token: Use {{hexcode}} to represent the hexadecimal codepoint.
    2. Function: Receives hexcode and an object containing { enlarged, smallSize, largeSize, size }.

    Note: SVGs require CORS. If hosting on a different domain, use PNGs instead.

    // Using a string path
    <Interweave
      emojiPath="https://example.com/images/emoji/{{hexcode}}.png"
      matchers={[new EmojiMatcher('emoji')]}
    />
    
    // Using a function path
    <Interweave
      emojiPath={(hexcode, { size }) => `https://example.com/images/emoji/${size}/${hexcode}.png`}
      matchers={[new EmojiMatcher('emoji')]}
    />
  10. Mock emoji data for testing

    master

    To avoid network requests during testing, use mockEmojiData() from interweave-emoji/test. This should be called during your test framework's bootstrap phase.

    Note: This requires the emojibase-test-utils dependency to be installed.

    import { mockEmojiData } from 'interweave-emoji/test';
    
    mockEmojiData();
    mockEmojiData('fr'); // For other locales
  11. Load emoji data with useEmojiData()

    master

    Emoji data must be loaded from the Emojibase CDN before rendering. Use the useEmojiData() hook to fetch this data. The hook returns a tuple: [emojis, source, manager].

    Pass the source returned by the hook to the emojiSource prop of your <Interweave /> component.

    import BaseInterweave, { InterweaveProps } from 'interweave';
    import { useEmojiData } from 'interweave-emoji';
    
    export default function Interweave(props: InterweaveProps) {
    	const [emojis, source, manager] = useEmojiData({ compact: false, shortcodes: ['emojibase'] });
    
    	return <BaseInterweave {...props} emojiSource={source} />;
    }