2025-06-25 Web Development
Building a Smooth Typing CLI Response Component in React
By O. Wolfson
When creating user interfaces, even small touches can dramatically affect how users perceive the responsiveness and friendliness of your app. One subtle but powerful UX enhancement is typed-out responses, instead of showing content instantly.
In this article, we’ll walk through building a simple but effective Command Line Interface (CLI)-style React component that responds to hardcoded commands with a smooth, context-aware typing animation — perfect for portfolios, chatbot experiments, or terminal-style playgrounds.
✨ What We’re Building
A React component that:
- Accepts user input like a terminal prompt
- Responds to predefined commands (e.g.
help
,about
,clear
,short
,medium
,long
) - Automatically adjusts its typing speed and granularity (character, word, or sentence) based on message length
- Displays all past interactions in a scrollable console-style interface
⚙️ Technologies Used
- React (with hooks)
- Tailwind CSS (for layout and theming)
- No external libraries — fully client-side
🧱 Component Breakdown
1. history
State
Stores all terminal lines — each one tagged as user input or system output.
tstype Line = { type: "input" | "output"; content: string };
This forms the scrollable history log you see in the console.
2. input
State
Tracks the current value of the user's text input.
3. pendingOutput
and isTyping
Control the output animation phase. When a command is submitted, the corresponding response is added to pendingOutput
, and the typing logic begins.
🧠 Smart Typing Modes
To avoid long delays for lengthy messages, the component adjusts how it renders responses based on their length:
Message Length | Mode | Behavior | Speed |
---|---|---|---|
≤ 200 chars | char | Renders letter by letter | 20ms/tick |
201–500 chars | word | Renders word by word | 50ms/tick |
> 500 chars | sentence | Renders sentence by sentence | 150ms/tick |
This keeps the animation effect pleasant without making long messages feel sluggish.
🎬 Typing Logic (Simplified)
tsuseEffect(() => {
const mode = getTypingMode(pendingOutput); // "char" | "word" | "sentence"
const parts = splitByMode(pendingOutput, mode); // split string by units
let index = 0;
const interval = setInterval(() => {
setHistory((prev) => {
const newContent = parts
.slice(0, index + 1)
.join(mode === "char" ? "" : " ");
const updated = [...prev];
const last = updated[updated.length - 1];
if (last?.type === "output") {
updated[updated.length - 1].content = newContent;
} else {
updated.push({ type: "output", content: newContent });
}
return updated;
});
index++;
if (index >= parts.length) {
clearInterval(interval);
setIsTyping(false);
setPendingOutput("");
}
}, getDelayForMode(mode));
}, [isTyping, pendingOutput]);
📖 Supported Commands
tsconst RESPONSES = {
help: `Available commands:\n- help\n- about\n- contact\n- clear\n- short\n- medium\n- long`,
about: `This is a simple demonstration of a command console component that uses a typewriter-style animation to render responses.
Typing animations can greatly enhance perceived interactivity and responsiveness in user interfaces, even in basic terminal-like environments.
This demo is written in React using functional components and hooks. It can easily be extended with additional commands, real-time data fetching, or even connected to a backend.
Try typing 'help' to see available commands, or 'clear' to reset the console.`,
contact: `Email: contact@example.com\nTwitter: @example`,
short: `This is short.`,
medium: `This is a medium-length message designed to demonstrate how each word appears one after the other with smooth timing, simulating a thoughtful typing pace.`,
long: `This is a long-form demonstration designed to show how sentence-based rendering improves readability and performance.
When a message exceeds a certain length, typing each individual character becomes tedious and unnecessary. Instead, we chunk the message into sentences and display them with a natural delay.
This balances responsiveness with user experience, especially in UIs where content is meant to be skimmed or understood quickly, like help messages or documentation previews.`,
};
🧼 Special Command: clear
Typing clear
wipes the console history instantly:
tsif (command === "clear") {
setHistory([]);
return;
}
🖥 Full User Flow
- User types a command and hits Enter
- If a response is found, it’s typed out with appropriate pacing
- Otherwise, the user sees a “Command not found” message
- History is preserved until
clear
is used
💡 Possible Extensions
You could easily expand this component with:
- ↑/↓ command history navigation
- Custom prompt indicators or cursor styles
- Sound effects per tick or typing chunk
- Integration with external APIs for dynamic data
- Markdown formatting (e.g., bold, headers, links)
- Theme toggles for dark/light terminal styles
🧪 Conclusion
This enhanced command console is a lightweight yet powerful tool for adding retro-flavored interactivity to any web app. By dynamically adjusting how responses are rendered, you get a UX that feels responsive for short replies and efficient for long-form content.
Perfect use cases:
- Developer portfolios
- AI/chatbot experiments
- Internal tools
- Retro-inspired UI demos