Customizable, smooth, and snappy typing animations for React — built for natural, human-like effects with minimal config.
npm install typingfx





!npm bundle size
> ⚡ 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
- ⚡ Smarter performance: animation pauses 💤 when the tab loses focus — saving CPU and improving battery life without breaking the flow.
- 🎨 Fully customizable cursor with auto-blink
- 🔁 Infinite/controlled looping
- 💡 Fully typed with TypeScript
- 🧩 SSR-safe and RSC-compatible
---
``bash`
pnpm add typingfx
_or_
`bash`
npm install typingfx
_or_
`bash`
yarn add typingfx
---
> 🎨 Required for typing animation and cursor styling.
In JSX (Next.js layout files recommended):
`tsx`
import "typingfx/dist/index.css";
Or in global CSS:
`css`
@import "typingfx/dist/index.css";
---
`tsx
import { TypeOut } from "typingfx";
export default function Example() {
return (
speed={25}
delSpeed={40}
repeat={Infinity}
/>
);
}
`
---
Need a one-off typing effect without using steps?
`tsx`
export default function Example() {
return (
I love {500} typingfx
);
}
> ⏱️ Use numbers inside JSX to insert pauses (in milliseconds) during typing.
> ➖ Negative numbers delay deletion.
> 🔤 Want to render the number instead? Wrap it with String() or use template literals.
---
`tsx`
<>
Building with React
>,
<>
Deploying on Vercel
>,
<>
Coding like a 💻
>,
]}
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.
`tsx`
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:
`tsx`
wrapper: "div",
props: { className: "custom-wrapper" },
}}>
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:
`html`
Server-rendered content
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:
`css
.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:
`tsx`
<>
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:
`tsx`
<>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 is memoized by default to prevent unwanted animation restarts and infinite loops.
`tsx`
> 🔁 It ignores all prop changes after first render, including paused, speed, etc.
- 🧊 Props are frozen on mount
- 🧠 Re-renders won't restart animation
- ✅ To re-run animation with new steps, change the key to remount
`tsx`
---
Want to update props on the fly (e.g., pause/resume, adjust speed) without triggering full React re-renders or loop traps?
Use the useUpdate() hook with double parentheses:
`ts
import { useUpdate } from "typeout";
export function Demo() {
const [paused, setPaused] = useUpdate("demo")(state => [state.paused, state.setPaused]);
// if you want to update props globally do not pass any id -> useUpdate()(state => [...])
return (
> 🎯
useUpdate() returns an isolated reactive store per id. These updates:
>
> - Take effect immediately
> - Don’t rely on props or re-renders
> - Work perfectly with interactive controls---
$3
- ⚠️
steps cannot be updated dynamically — changing them requires a remount via a new key.
- 💥 remounting via key will kill the current animation and start new one.
- ✅ All other animation props are supported via useUpdate().---
$3
| Setter | Description |
| ------------------------- | -------------------------------------- |
|
setSpeed(speed) | Typing speed (chars/sec) |
| setDelSpeed(delSpeed) | Deletion speed (chars/sec) |
| setNoCursor(boolean) | Toggle cursor visibility |
| setNoCursorAfterAnimEnd | Hide cursor after animation ends |
| setRepeat(count) | How many times to repeat the animation |
| setForce(boolean) | Override user’s reduced motion setting |
| setPaused(boolean) | Pause or resume |
| setComponentAnimation() | Update animation for custom components |> ℹ️ These updates are instance-local, fast, and non-intrusive.
> Use the
storeId prop to group/manage the animation props across TypeOut instances.---
$3
Each step can contain multiple elements like
,
, or fragments – typingfx will animate them line by line:`tsx
steps={[
<>
Hi there
Welcome to TypingFX!
>,
<>
Hi there
TypingFX is awesome!
>,
]}
/>
`> ✅ No need to split them into separate steps – group them as one step to animate fluidly across lines.
---
$3
When inserting delays between block elements, prefer placing the delay inside one block, rather than between them:
`tsx
// ❌ Avoid: causes extra spacing
<>
Hi
{5000}
Hello
>// ✅ Recommended
<>
Hi{5000}
Hello
>
// or
<>
Hi
{5000}Hello
>
`---
$3
Want to hide the blinking cursor?
`tsx
Hello, no cursor here!
`---
$3
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).
---
⚙️ Props
| 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. |---
📦 Framework Compatibility
- ✅ 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)---
❓ FAQ & Gotchas
⚠️ 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:`tsx
const steps = useMemo(() => ["Hello", "World"], []);
;
`---
📏 Why is there extra space when adding delays between elements?
Inserting a delay like
{500} between block elements (e.g. ) creates empty text nodes, leading to layout shifts.
❌ Bad:
` Hi Hellotsx`
<>
{500}
>
✅ Good:
` Hi{500} Hellotsx`
<>
>
> 📌 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
`tsx`
or
`tsx`
---
🎨 How do I animate styled or rich text?
TypingFX supports JSX out of the box! You can mix , , emojis, or even full components.
`tsx`
<>
Writing with style
>,
<>
Deployed via Vercel
>,
<>💻 Happy Coding!>,
]}
/>
> ✨ Fully supports React elements, fragments, and inline styles.
---
next was previously listed in optionalPeerDependencies` only to indicate compatibility. It has been removed:
- ✅ TypingFX still works perfectly in Next.js (including App Router, SSR, RSC)
- 🧼 Avoids triggering npm warnings in non-Next projects
- 📚 Compatibility now noted in docs + keywords only
MPL-2.0 open-source license © Mayank Chaudhari
>
Please enroll in our courses or sponsor our work.
with 💖 by Mayank Kumar Chaudhari