React Terminal Component: Typing Animation Without Jank
A React terminal component is the window on a dev-tool landing page where npm install types itself, a spinner turns into a check and the deploy URL prints in green. It shows what a CLI does faster than a paragraph can, and it is easy to get wrong: the hero jumps as lines appear, a screen reader hears nothing, the animation replays forever for people who asked for less motion, and a long command pushes the page sideways on a phone. This guide builds a React terminal UI that avoids all four with the Terminal from Wingo UI. We build that library, so we are not neutral; facts about other projects link to their sources and are dated.
What should a React terminal component do?
Show a believable session, stay out of the way and stay readable. Here is the list we built against:
- Typed commands after a prompt, with a block caret, at a speed you can set.
- Activity: a spinner that settles into a check (or a cross when the step failed) and a progress bar that fills to 100%.
- Toned output: dimmed by default, with single lines in success, warning or danger colors.
- Copy and replay buttons, so nobody retypes a command or reloads the page to watch the session again.
- No layout shift. The window has its final height before the first character types.
- Readable without JavaScript. The whole transcript is in the server HTML, for crawlers and screen readers alike.
- Reduced motion shows the finished session at once.
- Phone behavior: long lines scroll inside the window, never the page.
How do you add a terminal to a React app?
Install it with the CLI. It copies terminal.tsx into components/ui/ along with what it imports (Button, Copy Button, Tooltip and the motion helpers). Terminal is a Pro item, so log in first with npx wingo-ui@latest login:
Then describe the session as data. Each entry in lines is one step: a command to type, an optional spinner or progress label that runs for duration ms, and output lines as strings or { text, tone }.
Scroll the demo into view to watch it play, then press replay. Try the copy button too: it copies both commands, one per line.
$ npx wingo-ui@latest add terminalThe props that matter most: mode="dark" keeps the window dark on a light page (the classic hero look), chrome picks mac dots, a plain title bar or no chrome at all, tone or any CSS color paints the prompt, caret and progress bars, and prompt changes $ to ❯ or root@vps:~#. A step can set its own prompt and delay. If the copy button should copy something other than the commands, such as a one-line install script, pass it as copyValue.
How do you show a failing step?
Set tone: "danger" on the step. The spinner ends in a cross instead of a check, and the output lines can each carry their own tone:
A red line in a marketing terminal sounds odd, but it works well in docs for a "what an error looks like" section, and in a changelog that shows a bug before and after.
How does a React terminal animation avoid jank?
Keep React out of the per-character loop. The obvious way to build an animated terminal is a setInterval that appends one character to state. Magic UI's TypingAnimation, for example, starts from an empty string and calls setDisplayedText once per character, 60 ms apart by default (registry source (opens in a new tab), checked November 6, 2026). That is fine for one headline. A whole session of commands means a render every 45 to 60 ms for several seconds, and the text is not in the DOM until it has typed.
Wingo UI's Terminal works the other way around. The full transcript is laid out on the first render. One motion value holds the session clock in milliseconds, and every row derives its own state from it: a command reveals itself with a clip-path that grows one ch at a time, the caret moves with a transform, and output rows switch their opacity at their scheduled time. React renders only when playback starts and ends. Because every row already takes its final space, the window keeps the same height from the first frame to the last, so the content below it never shifts.
Two more rules keep it calm. With trigger="in-view" (the default) it waits until about a third of the window is on screen, so a terminal below the fold does not finish before anyone sees it. With trigger="mount" it starts as soon as the page has hydrated. With loop, it rests while it is off screen instead of burning frames.
What happens with reduced motion and screen readers?
Under prefers-reduced-motion: reduce, the Terminal shows the finished transcript at once and disables the replay button, since there is nothing to replay. It reads the preference through usePrefersReducedMotion from lib/motion, which is hydration safe; we explain why motion's own hook is not in Prefers reduced motion in React. animate={false} gives the same still result on purpose, for example in a thumbnail or a printed docs page.
For screen readers, the transcript is a focusable region named by the title (or labels.region, "Terminal" by default). Rows that are not visible yet still contain their text, so the whole session is read in order. Prompts, dots, the caret and the spinner frames are aria-hidden, so a reader hears the commands and the output, not "dollar" and braille dots. The copy button announces "Copied" through the Copy Button it is built on.
Cult UI made a different, reasonable choice: its Terminal Animation marks the output role="log" with aria-live="polite", so new lines are announced as they appear (docs (opens in a new tab), checked November 6, 2026). That suits output a user triggered. For a hero that plays on its own, we preferred silence over a reader announcing a fake deploy line by line.
How does the terminal behave on a phone?
Lines never wrap, because a wrapped shell command looks broken and copies badly. Instead the viewport scrolls sideways inside the window, with overscroll-x-contain so a swipe does not drag the page, and keyboard users can Tab to it and use the arrow keys. Below 640px the type steps down one size (12px becomes 11px for size="sm", 13px becomes 12px for md), which fits a typical npm install line on a 390px screen. The copy and replay buttons are regular Button components at the small icon size, which keeps a 32px look and adds a 44px hit area on a coarse pointer.
One pitfall: the typing reveal counts characters in ch, the width of one monospace character. Keep emoji and other wide characters out of command strings; they are fine in output, which does not type.
How do you control playback from your own button?
Pass playing and onPlayingChange. With defaultPlaying={false} the window waits on an empty prompt until you start it, and onComplete fires every time a session finishes:
onPlayingChange(false) arrives at the end of a session that does not loop, so the button label flips back on its own.
What terminal components exist for shadcn projects?
shadcn/ui itself has none, so a shadcn terminal component comes from a third-party registry or from npm. What their docs, source and npm pages showed on November 6, 2026:
Where the others are the better pick: Magic UI and Cult UI are free and MIT licensed, and the Wingo UI Terminal is not. Magic UI composes any JSX as a line, which is handy for custom output. Cult UI has tabbed scenarios built in (npm, pnpm and yarn tabs, say) and a background image option. react-animated-term is a plain npm dependency with no registry, if you can live with a package that has not shipped a release since 2021.
When is a code block or a text animation the better choice?
When the reader should act on the text. An animated terminal is a demo; the steps of a setup guide belong in a static, highlighted Code Block with a copy button, which is free:
$ npx wingo-ui@latest add code-blockAnd when you only want a headline to type itself, the full window is too much. Text Animate with effect="typewriter" types one string with a caret, keeps the element a real h1 through as, and reads the full sentence to screen readers once.
When the output is real, streamed from a build or a server, skip the animation altogether. Our React log viewer guide shows live lines with level filters and a follow mode instead.
Where does a terminal fit on a landing page?
In the hero, next to the headline, or in a "how it works" band right under it. Keep it to two or three steps; a session that runs longer than about eight seconds loses the reader before the payoff line. Put the real install command in a code block or a copy pill nearby, so nobody has to wait for the animation to copy it. Our shadcn hero section guide covers the layouts around it.
If you are building a whole developer platform page, the Developers Page template already has a code hero with language tabs, API surface cards, an endpoint explorer and a CLI terminal that plays deploy and webhook lines when it scrolls into view.
More guides like this one are in the component guides category, starting with the pillar on React form components.
Next step
Open the Terminal docs and try chrome, tone, speed and loop in the playground, then install it with npx wingo-ui@latest add terminal. Terminal and the Developers Page are part of Wingo UI Pro (pricing); the Code Block and Copy Button are free with a free account.
FAQ
How do I make an animated terminal in React?
Describe the session as data (each step a command, optional spinner or progress bar, and output lines) and let a component play it on a timeline. With Wingo UI that is npx wingo-ui@latest add terminal and a lines array passed to the Terminal component.
Does shadcn/ui have a terminal component?
No. As of November 6, 2026, the shadcn/ui component list has no terminal, while Magic UI and Cult UI publish terminal components through the shadcn registry and Wingo UI installs its Terminal as source code through its own CLI.
Is an animated terminal bad for accessibility?
It can be. Text that builds up one character at a time is announced in fragments or not at all, and motion bothers some readers. Render the full text from the start, reveal it visually only, and skip the animation under prefers-reduced-motion.
Should I use a terminal or a code block for install commands?
Use a code block when people should read and copy a command, and a terminal when you want to show what happens after they run it. A docs page can use both: the code block in the setup steps, the terminal in the hero.
Is the Wingo UI Terminal free?
No, Terminal is part of Wingo UI Pro ($8 a month, $80 a year or $150 once). The Code Block and Copy Button it pairs with are free with a free account.
- Components
- React
- Animation
- Landing page
- Docs


