Elevate your type dynamic

A responsive text animation library for React, built around a ready-to-use component with six interchangeable animation effects.

Up your game with text reveal

Text changes. Screens shrink. Fonts arrive late. Your animation should keep up. Txtrvl follows the browser’s actual visual lines, from two lines to ten, without splitting your text by hand.

01

Six effects. One component. Your choice.

02

Follows changes in text and container width

03

Adapts to font sizes and web font loading

04

Real line wrapping, newlines and long words

05

Your typography, colors and timing

06

Scroll triggers and React state controls

07

Reduced motion and accessible source text

08

TypeScript, Next.js, ESM and CommonJS

Live preview

Six effects, real browser line wrapping. Choose an effect, play it, then squeeze the width or change the words.

Six effects. One React component. Your typography deserves a little movement.

0 visual lines · Scroll trigger: hidden

The first animation starts when the text scrolls into view, then switches to Manual trigger. Use Play, Reset and Replay, or turn Manual trigger off to return to scrolling. Your reduced-motion preference is respected: text appears without transitions.

Installation

To install our component, you only need to go through these twenty steps. Kidding, there are only two.

STEP 1

Install

Install the library with npm install txtrvl. It includes a ready-to-use React component, six interchangeable effects and TypeScript types. React and React DOM 16.14.0 or newer are supported.

STEP 2

Use it

Use the named import: import { Txtrvl } from "txtrvl". Choose an effect with animation, set timing with duration and delayPerRow, and bring your own color and typography through style or className. The default effect is reveal.

import { Txtrvl } from "txtrvl";

export function Heading({ manualTextRevealState = false }) {
  return (
    <Txtrvl
      text="Your words. Your timing."
      animation="slide-up"
      duration={1000}
      delayPerRow={200} // ms between lines
      className="animated-heading"
      style={{ fontSize: "7rem", color: "red" }}
      // Scroll visibility, not completion
      onChange={(isVisible) => console.log(isVisible)}
      manualTrigger={{ // optional config
        isVisible: manualTextRevealState, // true | false
      }}
      scrollTrigger={{
        root: null, // Viewport or ancestor
        threshold: 0.5, // Visible area ratio
        offsetY: 0, // Expand / shrink (px)
        delay: 0,
        // "above", "both" or "no-reset"
        resetPolicy: "above",
        disabled: false, // true: manual only
      }}
    />
  );
}

One component, all the controls

Txtrvl 1.1.8 brings together the animation and layout work originally prepared for unreleased 1.1.7, plus the published entry-point and packaging updates. All six effects share the same API.

Txtrvl props
PropTypeDefaultWhat it does
textstringRequiredText to animate, including optional newlines.
animationTxtrvlAnimation"reveal"reveal, fade, slide-up, slide-left, blur or scale.
durationnumber1000Transition duration in milliseconds.
delayPerRownumber200Delay between successive visual lines, in milliseconds.
styleReact.CSSProperties—Container dimensions, color and typography.
classNamestring—Additional CSS class for the container.
onChange(isVisible: boolean) => void—Called when visibility detected by the scroll trigger changes.
manualTrigger{ isVisible: boolean }—Control visibility through React state.
scrollTriggerScroll configurationEnabledOptions below control automatic visibility.

TypeScript exports: TxtrvlProps and TxtrvlAnimation. React and React DOM 16.14.0 or newer are supported.

scrollTrigger options
PropTypeDefaultWhat it does
rootElement | nullnullViewport by default; optionally supply an ancestor DOM element.
thresholdnumber0.5Fraction of the maximum visible area that fits in the viewport and clipping containers.
offsetYnumber0Expand or shrink the vertical detection bounds, in pixels.
delaynumber0Wait this many milliseconds before changing visibility; cancelled if the condition changes.
resetPolicy"above" | "both" | "no-reset""above""above" resets when text goes below the visible area; "both" resets on either exit; "no-reset" keeps revealed text visible.
disabledbooleanfalseDisable automatic triggering for manual playback alone.

Scrolling uses IntersectionObserver and accounts for clipping by scrollable containers. For long text, the threshold uses the area that can fit inside the visible container. When both triggers are active, either the scroll trigger or manual state can reveal the text.

Play it yourself

For a button to control playback on its own, disable scrolling and pass your state through manualTrigger. Reset before playing again.

import { useState } from "react";
import { Txtrvl, type TxtrvlAnimation } from "txtrvl";

export function PlayableHeading() {
  const [isVisible, setIsVisible] = useState(false);
  const animation: TxtrvlAnimation = "fade";

  return (
    <>
      <button onClick={() => setIsVisible((visible) => !visible)}>
        {isVisible ? "Reset animation" : "Play animation"}
      </button>
      <Txtrvl
        text="Your words. Your timing."
        animation={animation}
        scrollTrigger={{ disabled: true }}
        manualTrigger={{ isVisible }}
        style={{ width: "100%", maxWidth: "48rem" }}
      />
    </>
  );
}

React, Next.js & your styles

Use the named import in ESM, or const { Txtrvl } = require("txtrvl") in CommonJS. Styles are included automatically; an explicit import "txtrvl/styles.css" is also available.

Published entry points include "use client" for Next.js App Router. Put callbacks and interactive React state in your own Client Component. Set colors, fonts and dimensions through style or className.

Need the source of truth? Read the README, public types or AI-friendly docs.

FAQ

No one ever asks us anything, so we had to write everything ourselves so the bot would have something to read.

Credits

If you like what you see, write to us and let's build or destroy something together.

LET'S TALK

Build something.
Or break the rules.

Got a question, an idea, or a project in mind? Tell us about it.

We'll use your details to reply to this message. Privacy policy ↗