react-mark.js

🍬 A React wrapper around the popular mark.js library.

Usage no npm install needed!

<script type="module">
  import reactMarkJs from 'https://cdn.skypack.dev/react-mark.js';
</script>

README

React Mark JS

🍬 A React wrapper around the popular mark.js library.


Contents

Single Page Doc

⚑️ Installation

The best way to install react-mark.js is via the npm package which you can install with npm (or yarn if you prefer)

πŸ“¦ NPM

npm install -S mark.js react-mark.js

πŸ“¦ Yarn

yarn add mark.js react-mark.js

NOTE: mark-js is a peer dependency

β†˜οΈ Importing Components

You can choose to import single components like react-mark.js/Marker rather than the entire component library.

import Marker from "react-mark.js/Marker";
// or; if you prefer
import { Marker } from "react-marker.js";

πŸ–Œ Basic Example

import Marker from "react-mark.js/Marker";
// or; if you prefer
import { Marker } from "react-mark.js";

export default () => (
  <Marker mark="reprehenderit">
    Cillum proident eu eiusmod incididunt pariatur. 
    Ullamco qui deserunt ut reprehenderit cupidatat cupidatat 
    nisi non occaecat non commodo. Magna 
    incididunt eu laboris laboris labore. Sit duis 
    ullamco qui nostrud aliqua do
    consectetur do incididunt eiusmod nulla consectetur. 
    Sint reprehenderit culpa consectetur irure commodo magna. 
    Officia Lorem veniam est cillum.
  </Marker>
);

πŸ’‡β€β™‚οΈ Styling

When you wrap a component with <Marker />; the text-content that you are trying to highlight will automatically be wrapped with <mark /> HTML Element.

Mark Elements usually comes with default styles and that may be enough for some use cases.

Custom configuration for this inline <mark /> element depends on the style-framework that is integrated in your application.

CSS - Single Element (demo β†—)

If you have your styles in a CSS file; you can simply add styles for the mark element in your CSS stylesheet:

mark {
    background-color: deeppink;
}

If you multiple markers on your page and want to style each one differently that too is possible.

CSS - Multiple Elements (demo β†—)

If you want to mark each element differently you need to pass a className in the options object to <Marker /> element and then add the styles for each class.

<Marker
    options={{className: 'custom-marker-1'}}
    mark="hello"
>
    Hello World
</Marker>

<Marker
    options={{className: 'custom-marker-2'}}
    mark="hello"
>
    Hello World
</Marker>

Now, add the CSS styles for each class

mark.custom-marker-1 {
  background-color: orange;
  padding: 2px;
}

mark.custom-marker-2 {
  background-color: lightgreen;
  padding: 2px;
}

Styled Components (demo β†—)

You can simply pass the Marker to the styled component and style it. For ex:

import styled from "styled-components";

export const OrangeMarker = styled(Marker)`
  mark {
    background-color: orange !important;
  }
`;

export const LightGreenMarker = styled(Marker)`
  mark {
    background-color: lightgreen;
  }
`;

πŸŽ› Components

⚑️ <Marker /> Stack Blitz Demo β†—

A wrapper component to higlight text-content matching strings.

import React from "react";
import Marker from "react-mark.js/Marker"; // or, import {Marker} from 'react-mark.js'

const MarkerExamples = () => {
  return (
    <div>
      <h2>Single String</h2>
      <Marker mark="Ullamco qui deserunt ut reprehenderit">
        Cillum proident eu eiusmod incididunt pariatur. 
        Ullamco qui deserunt ut
        reprehenderit cupidatat cupidatat nisi non 
        occaecat non commodo. Magna
        incididunt eu laboris laboris labore.
      </Marker>
      <h2>Multiple Strings</h2>
      <Marker mark={["eiusmod incididunt", "commodo. Magna"]}>
        Cillum proident eu eiusmod incididunt pariatur. 
        Ullamco qui deserunt ut
        reprehenderit cupidatat cupidatat nisi non occaecat 
        non commodo. Magna
        incididunt eu laboris laboris labore.
      </Marker>
      <h2>Configure Wrapper Element</h2>
      <Marker 
        as="section" 
        mark="Ullamco qui deserunt ut reprehenderit"
      >
        Cillum proident eu eiusmod incididunt pariatur. 
        Ullamco qui deserunt ut reprehenderit cupidatat 
        cupidatat nisi non occaecat non commodo. 
        Magna incididunt eu laboris laboris labore.
      </Marker>
      <h2>With Options</h2>
      <Marker
        as="section"
        mark="nisi non occaecat"
        options={{
          className: "custom-marker-1"
        }}
      >
        Cillum proident eu eiusmod incididunt pariatur. 
        Ullamco qui deserunt ut reprehenderit cupidatat 
        cupidatat nisi non occaecat non commodo. Magna
        incididunt eu laboris laboris labore.
      </Marker>
    </div>
  );
};

export default MarkerExamples;


⚑️ <RegExpMarker /> Stack Blitz Demo β†—

A wrapper component to higlight strings matching a regular expression.

import React from "react";
import { RegExpMarker } from "react-mark.js";

const RegExpMarkerExamples = () => (
  <div>
    <h2>Marking with RegExp</h2>
    <RegExpMarker mark={/qui/}>
      Cillum proident eu eiusmod incididunt pariatur. Ullamco qui deserunt ut
      reprehenderit cupidatat cupidatat nisi non occaecat non commodo. Magna
      incididunt eu laboris laboris labore.{" "}
    </RegExpMarker>
  </div>
);

export default RegExpMarkerExamples;

⚑️ <RangesMarker /> Stack Blitz Demo β†—

Sometimes you may want to mark text-content based on the position in the text content instead of a string. For ex. you would like to mark 5 letters from the 3rd position of text.

import React from "react";
import RangesMarker from "react-mark.js/RangesMarker";
// or import {RangesMarker} from 'react-mark.js'
const blue = { color: "blue" };
export const RangesExample = () => {
  return (
    <div>
      <h2>Single Range</h2>
      <RangesMarker
        style={blue}
        mark={[
          {
            length: 5,
            start: 3
          }
        ]}
      >
        <h3>0123456789</h3>
      </RangesMarker>
      <h2>Multiple Ranges</h2>
      <RangesMarker
        style={blue}
        mark={[
          {
            length: 5,
            start: 3
          },
          {
            length: 15,
            start: 11
          }
        ]}
      >
        <h3>0123456789 123456789 123456789 123456789</h3>
      </RangesMarker>
      <h2>With Options</h2>
      <RangesMarker
        mark={[
          {
            length: 5,
            start: 3
          },
          {
            length: 15,
            start: 11
          }
        ]}
        options={{
          className: "custom-marker-1"
        }}
      >
        <h3 style={blue}>0123456789 123456789 123456789 123456789</h3>
      </RangesMarker>
    </div>
  );
};

export default RangesExample;

πŸͺ Hooks

useMarker Stack Blitz Demo ↗️

This recipe is for more custom configuration as you can access the markerRef and marker (which is a MarkJS instance).

  • markerRef - you would pass this to the component wrapping your element.
  • marker - a MarkJS instance which has an API for marking and un-marking.

Lets dive into an example which will make things more clearer.

import React from "react";
import { useMarker } from "react-mark.js";

const MyContent = () => {
  const { markerRef, marker } = useMarker();

  React.useEffect(() => {
    if (marker) {
      marker.mark("orl");
    }
  }, [marker]);

  const mark = React.useCallback(() => {
    marker.mark("Hello"); // https://markjs.io#mark
  }, [marker]);

  const unmark = React.useCallback(() => {
    marker.unmark(); // https://markjs.io#unmark
  }, [marker]);

  return (
    <div ref={markerRef}>
      Hello World
      <button onClick={mark}>Mark</button>
      &nbsp;
      <button onClick={unmark}>Unmark</button>
    </div>
  );
};

export default MyContent;

Options

⚑️ options (optional prop)

You can pass any of these options to the options props (source - mark.js):

OptionTypeDefaultDescription
elementstring"mark"HTML element to wrap matches, e.g. span
classNamestring""A class name that will be appended to element
excludearray[ ]An array with exclusion selectors. Matches inside these elements will be ignored. Example: "filter": ["h1", ".ignore"]
separateWordSearchbooleantrueWhether to search for each word separated by a blank instead of the complete term
accuracystring or object"partially"Either one of the following string values:
  • "partially": When searching for "lor" only "lor" inside "lorem" will be marked
  • "complementary": When searching for "lor" the whole word "lorem" will be marked
  • "exactly": When searching for "lor" only those exact words with a word boundary will be marked. In this example nothing inside "lorem". This value is equivalent to the previous option wordBoundary
Or an object containing two properties:
  • "value": One of the above named string values
  • "limiters": A custom array of string limiters for accuracy "exactly" or "complementary". Read more about this in the tutorials section
diacriticsbooleantrueIf diacritic characters should be matched. For example "piΔ™kny" would also match "piekny" and "doner" would also match "dΓΆner"
synonymsobject{}An object with synonyms. The key will be a synonym for the value and the value for the key. Example: synonyms: {"one": "1"} will add the synonym "1" for "one" and vice versa
iframesbooleanfalseWhether to search also inside iframes. If you don't have permissions to some iframes (e.g. because they have a different origin) they will be silently skipped. If you don't want to search inside specific iframes (e.g. facebook share), you can pass an exclude selector that matches these iframes
iframesTimeoutnumber5000The maximum ms to wait for a load event before skipping an iframe. Especially important when there's no internet connection or a browser "offline" mode is enabled and an iframe has an online src – then the load event is never fired
acrossElementsbooleanfalseWhether to search for matches across elements
caseSensitivebooleanfalseWhether to search case sensitive
ignoreJoinersbooleanfalseWhether to also find matches that contain soft hyphen, zero width space, zero width non-joiner and zero width joiner. They're used to indicate a point for a line break where there isn't enough space to show the full word
ignorePunctuationarray[ ]An array of punctuation mark strings. These punctuation marks can be between any characters, e.g. setting this option to ["'"] would match "Worlds", "World's" and "Wo'rlds". One or more apostrophes between the letters would still produce a match (e.g. "W'o''r'l'd's"). A typical setting for this option could be as follows: ":;.,-–—‒_(){}[]!'\"+=".split("")
wildcardsstring"disabled"Set to any of the following string values:
  • "disabled": Disable wildcard usage
  • "enabled": When searching for "lor?m", the "?" will match zero or one non-space character (e.g. "lorm", "loram", "lor3m", etc). When searching for "lor*m", the "*" will match zero or more non-space characters (e.g. "lorm", "loram", "lor123m", etc).
  • "withSpaces": When searching for "lor?m", the "?" will match zero or one space or non-space character (e.g. "lor m", "loram", etc). When searching for "lor*m", the "*" will match zero or more space or non-space characters (e.g. "lorm", "lore et dolor ipsum", "lor: m", etc).
eachfunctionA callback for each marked element. Receives the marked DOM element as a parameter
filterfunctionA callback to filter or limit matches. It will be called for each match and receives the following parameters:
  1. The text node which includes the match
  2. The term that has been found
  3. A counter indicating the total number of all marks at the time of the function call
  4. A counter indicating the number of marks for the term
The function must return false if the mark should be stopped, otherwise true
noMatchfunctionA callback function that will be called when there are no matches. Receives the not found term as a parameter
donefunctionA callback function after all marks are done. Receives the total number of marks as a parameter
debugbooleanfalseSet this option to true if you want to log messages
logobjectconsoleLog messages to a specific object (only if debug is true)

⚑️ unmarkOptions (optional prop)

Any of the following options can be passed to the unmarkOptions prop in the available components or hook (source - mark.js):.


Option Type Default Description
element string "" Will remove only marked elements with this specific element
className string "" Will remove only marked elements with this specific class name
exclude array [ ] An array with exclusion selectors. These elements will be ignored. Example: "filter": ["h1", ".ignore"]
iframes boolean false Whether to search also inside iframes. If you don't have permissions to some iframes (e.g. because they have a{" "} different origin ) they will be silently skipped. If you don't want to search inside specific iframes (e.g. facebook share), you can pass an exclude selector that matches these iframes
iframesTimeout number 5000 The maximum ms to wait for a load event before skipping an iframe. Especially important when there's no internet connection or a browser "offline" mode is enabled and an iframe has an online{" "} src – then the load event is never fired
done function A callback function after all marked elements were removed
debug boolean false Set this option to true if you want to log messages
log object console Log messages to a specific object (only if debug is true)