/ react

Building AutoLink component in React

Today, I had to implement a React component which takes text and adds links to the detected URLs:

<AutoLink>
  This text contains link to http://example.com and https://example.com
</AutoLink>

This generates the following HTML:

<p>
  This text contains link to <a href="http://example.com" target="_blank">http://example.com</a> and <a href="https://example.com" target="_blank">https://example.com</a>
</p>

In order to build this component, I had to use а little know feature of the String.prototype.split method. Most people know that if you have the following string a,c,d,e you can split it by ,, you get an array:

"a,b,c".split(/,/) // => ["a", "b", "c"]

Today I learned, that you can actually include the separator in the results:

"a,b,c".split(/(,)/) // => ["a", ",", "b", ",", "c"]

This uses the regular expression group feature.

Using this feature, I created a mapLinks function. It takes the text and yields the links it detects.

const EXP = /(https?:\/\/(?:www\.|(?!www))[a-zA-Z0-9][a-zA-Z0-9-]+[a-zA-Z0-9]\.[^\s]{2,}|www\.[a-zA-Z0-9][a-zA-Z0-9-]+[a-zA-Z0-9]\.[^\s]{2,}|https?:\/\/(?:www\.|(?!www))[a-zA-Z0-9]+\.[^\s]{2,}|www\.[a-zA-Z0-9]+\.[^\s]{2,})/gi; 

export function mapLinks(text, fn)) {
  // this splits the text, by links but it still keeps those links in the result
  return text.split(EXP).map((chunk, i) => {
    if (chunk.match(EXP)) {
      return fn(chunk, i);
    }

    return chunk;
  });

Then I just used this function for the component itself:

export default React.memo(({ children }) => (
  <p>
    {mapLinks(children, (url, i) => (
      <a href={url} target="_blank" key={i}>
        {url}
      </a>
    ))}
  </p>
)}