GitHunt
ST

stevenvachon/react-splitting

A re-implementation of splitting.js (a text segmenter for animation) for React supporting SSR/SSG.

react-splitting NPM Version Build Status Coverage Status

An (incomplete) re-implementation of splitting.js (a text segmenter for animation) for React supporting SSR/SSG.

This is a re-implementation because the original works with real DOM nodes. Converting ReactNodes to such then back again might preserve most--if not all--attributes, but will definitely lose all React event handlers.

Warning

  • Only "chars" and "words" are supported for the by property.
  • <script> and <style> elements are property ignored.
  • The key option was renamed to cssKey to avoid a conflict with React's own key property.

Note

  • This library comes in two variants: one with "meta" and one without. The "meta" variant is a drop-in replacement for the original library as it replicates its CSS classes, CSS variables, data-* attributes and some quirky whitespace handling.
  • Each variant can be used as a component--which offers nothing additional--or as a function--which adds segment counts and access to segment nodes before they're rendered.

Consumer Usage

Installation

npm install react-splitting

<Splitting /> / splitting()

Lightweight and completely customizable.

import { Segmentation, Splitting } from 'react-splitting';

export default () => (
  <>
    <p>
      <Splitting by={Segmentation.WORDS}>
        Text <strong>split</strong> by <em>words</em>.
      </Splitting>
    </p>
    <p>
      <Splitting by={Segmentation.CHARS}>
        Text <strong>split</strong> by <em>characters</em>.
      </Splitting>
    </p>
    With more customization:
    <p>
      <Splitting
        by={Segmentation.WORDS}
        wordProps={i => ({ className: 'word', style: { '--word-index': i } })}
      >
        Text <strong>split</strong> by <em>words</em>.
      </Splitting>
    </p>
    <p>
      <Splitting
        by={Segmentation.CHARS}
        charProps={i => ({ className: 'char', style: { '--char-index': i } })}
        wordProps={i => ({ className: 'word', style: { '--word-index': i } })}
      >
        Text <strong>split</strong> by <em>characters</em>.
      </Splitting>
    </p>
  </>
);
import { Segmentation, splitting } from 'react-splitting';

export default () => {
  const { charCount, segments, wordCount } = splitting(
    <>
      Text <strong>split</strong> by <em>words</em>.
    </>,
    {
      by: Segmentation.WORDS,
      charProps: i => ({ className: 'char', style: { '--char-index': i } }),
      wordProps: i => ({ className: 'word', style: { '--word-index': i } }),
    }
  );
  return <p>{segments}</p>;
};

<SplittingWithMeta /> / splittingWithMeta()

Drop-in replacement.

import { SplittingWithMeta } from 'react-splitting/with-meta';

export default () => (
  <SplittingWithMeta as="div">
    Text with <strong>added metadata</strong>, split by <em>characters</em>.
  </SplittingWithMeta>
);
import { splittingWithMeta } from 'react-splitting/with-meta';

export default () => {
  const { charCount, container, wordCount } = splittingWithMeta(
    <>
      Text with <strong>added metadata</strong>, split by <em>characters</em>.
    </>,
    { as: 'div' }
  );
  return container;
};

Development Usage

Production Build

npm run build

Testing

The test suite can perform a single run:

npm test

… or indefinitely as files are changed:

npm run test:watch

To Do

  • Test with languages that don't use normal whitespace delimiters, such as Japanese, I think. Check out Intl.Segmenter's "word" granularity.
  • Probably add ESLint.
  • Remove tsc-alias and use rewriteRelativeImportExtensions with allowImportingTsExtensions when possible.

Languages

TypeScript99.4%JavaScript0.6%

Contributors

MIT License
Created November 16, 2025
Updated March 10, 2026
stevenvachon/react-splitting | GitHunt