β‘ Customizable, smooth, and snappy typing animations for React
Animate your text like a pro β fully compatible with React 18/19, Next.js 14/15, and React Server Components.
- π― Built for modern React (18/19) and Next.js (14/15)
- β¨ Smooth, realistic word-by-word animation
- π Step-based sequences with infinite looping
- π JSX-ready β animate styled, rich text effortlessly
- π§ Honors
prefers-reduced-motion
accessibility setting - β‘ Hybrid CSS + JS for best performance
- π‘ Fully typed with TypeScript
- π§© SSR-safe and RSC-compatible
- π« No runtime dependencies
pnpm add typingfx
or
npm install typingfx
or
yarn add typingfx
π¨ Required for typing animation and cursor styling.
In JSX (Next.js layout files recommended):
import "typingfx/dist/index.css";
Or in global CSS:
@import "typingfx/dist/index.css";
import { TypeOut } from "typingfx";
export default function Example() {
return (
<TypeOut
steps={["Frontend Developer.", "React Enthusiast.", "Open Source Advocate."]}
speed={25}
delSpeed={40}
repeat={Infinity}
/>
);
}
Need a one-off typing effect without using steps
?
export default function Example() {
return (
<TypeOut>
I love {500} <strong>typingfx</strong>
</TypeOut>
);
}
β±οΈ Use numbers inside JSX to insert pauses (in milliseconds) during typing.
β Negative numbers delay deletion.
π€ Want to render the number instead? Wrap it withString()
or use template literals.
<TypeOut
steps={[
<>
Building with <strong>React</strong>
</>,
<>
Deploying on <code>Vercel</code>
</>,
<>
Coding like a <span className="emoji">π»</span>
</>,
]}
speed={30}
repeat={3}
/>
TypingFX supports animating React components using a typing and deleting effect. This feature is currently in beta, and feedback is welcome.
By default, TypingFX assumes the component is pure and attempts to extract and animate its JSX output, treating it like static content. This provides a natural typing effect as if the component was written inline.
<TypingFX>{<MyPureComponent />}</TypingFX>
This enables smooth, character-by-character typing that matches the surrounding text.
For components with side effects or dynamic behavior, you can control their animation using the componentAnimation
prop:
<TypingFX
componentAnimation={{
wrapper: "div",
props: { className: "custom-wrapper" },
}}>
<MyComponent />
</TypingFX>
TypingFX will wrap the component with the specified element and apply:
- Fade-in (typing): 5s
- Fade-out (deleting): 3s
This disables JSX extraction and uses a wrapper-based animation strategy.
TypingFX cannot detect components in SSR environments. Thus, by default, SSR-rendered components are treated as normal content and animated using the default typing animation.
However, you can manually mark any DOM element to be treated as a component by adding a data-tfx
attribute with any truthy value:
<span data-tfx="true">Server-rendered content</span>
Combined with the componentAnimation
prop, this enables custom animation support even for SSR-rendered output.
You can override the fade animation by targeting the default class names:
.tfx_component.tfx_type {
animation: myCustomFadeIn 2s ease-in;
}
.tfx_component.tfx_del {
animation: myCustomFadeOut 2s ease-out;
}
We're exploring the best API design for component animation. If you have ideas or requirements, please open an issue or comment on this discussion.
You can embed numbers (e.g. {1000}
) directly inside JSX (steps
or children
) to add typing or deleting delays:
<TypeOut
steps={[
<>
Hello{800}
{-500}
</>,
"World!",
]}
/>
{800}
β pauses for 800ms while typing{-500}
β pauses for 500ms while deleting
β οΈ Important: Numbers must be embedded directly as JSX expressions β not as strings.
If you want to display a number instead of pausing, convert it to a string:
<>I waited {String(800)} milliseconds.</>
To prevent unintended animation restarts on re-renders, memoize your steps
or children
using useMemo
:
const steps = useMemo(() => ["One", "Two", "Three"], []);
<TypeOut steps={steps} />;
This is especially useful in dynamic React apps or when props change frequently.
Each step can contain multiple elements like <p>
, <div>
, or fragments β typingfx
will animate them line by line:
<TypeOut
steps={[
<>
<p>Hi there</p>
<p>Welcome to TypingFX!</p>
</>,
<>
<p>Hi there</p>
<p>TypingFX is awesome!</p>
</>,
]}
/>
β No need to split them into separate steps β group them as one step to animate fluidly across lines.
When inserting delays between block elements, prefer placing the delay inside one block, rather than between them:
// β Avoid: causes extra spacing
<>
<p>Hi</p>
{5000}
<p>Hello</p>
</>
// β
Recommended
<>
<p>Hi{5000}</p>
<p>Hello</p>
</>
// or
<>
<p>Hi</p>
<p>{5000}Hello</p>
</>
Want to hide the blinking cursor?
<TypeOut noCursor>Hello, no cursor here!</TypeOut>
No extra setup needed β TypeOut
is already marked as a client component.
β Works out of the box with Next.js 14/15 and React Server Components (RSC).
Prop | Type | Default | Description |
---|---|---|---|
steps |
ReactNode[] |
[""] |
The sequence of text or elements to animate. |
speed |
number |
20 |
Typing speed (characters per second). |
delSpeed |
number |
40 |
Deletion speed (characters per second). |
repeat |
number |
Infinity |
Number of times to loop over steps. |
noCursor |
boolean |
false |
Disables the blinking cursor. |
paused |
boolean |
false |
Manually pause or resume animation. |
force |
boolean |
false |
Forces animation even when prefers-reduced-motion is on. |
children |
ReactNode |
"" |
An optional initial step to animate. |
... | HTMLDivProps |
β | Additional props are passed to the container element. |
- β React 16.8 β React 19
- β Next.js 12 β Next.js 15
- β
SSR-safe (no
window
access during render) - β RSC-compatible (used as a client component)
β οΈ Why does the animation restart on every re-render?
This usually happens when steps
or children
are not memoized, causing a new reference on every render.
β
Fix: Use useMemo
to ensure stable references:
const steps = useMemo(() => ["Hello", "World"], []);
<TypeOut steps={steps} />;
π Why is there extra space when adding delays between elements?
Inserting a delay like {500}
between block elements (e.g. <p>
) creates empty text nodes, leading to layout shifts.
β Bad:
<>
<p>Hi</p>
{500}
<p>Hello</p>
</>
β Good:
<>
<p>Hi{500}</p>
<p>Hello</p>
</>
π Tip: Always place delays inside a block to avoid glitches.
π§© Does it work with RSC & Next.js 14/15?
Absolutely. TypeOut
is already marked as a client component, so it works out of the box with:
- β React Server Components (RSC)
- β App Router
- β Server-side Rendering (SSR)
βͺ How do I add delay during deleting?
Use negative numbers like {-500}
anywhere in the content.
{800}
β pause for 800ms while typing{-500}
β pause for 500ms while deleting
<TypeOut steps={["Start typing...", -1000, "Deleting now..."]} />
or
<TypeOut>Wait before deleting me{-500}</TypeOut>
π¨ How do I animate styled or rich text?
TypingFX supports JSX out of the box! You can mix <strong>
, <code>
, emojis, or even full components.
<TypeOut
steps={[
<>
Writing with <strong>style</strong>
</>,
<>
Deployed via <code>Vercel</code>
</>,
<>π» Happy Coding!</>,
]}
/>
β¨ Fully supports React elements, fragments, and inline styles.
MPL-2.0 open-source license Β© Mayank Chaudhari
Please enroll in our courses or sponsor our work.
with π by Mayank Kumar Chaudhari