Introducing Nuvyx UI v1.0.0

Interactive Terminal

A customizable terminal component with animated output and command processing.

Interactive Terminal Components

Beautiful, customizable terminal simulations for your web applications. Engage users with interactive command-line experiences.

Execute this command: sudo hack --target=mainframe
>

Hacker Terminal

Create immersive cybersecurity simulations

Installation Guide

1

Install Dependencies

Lucide React

npm install lucide-react
2

Copy Component Code

Interactive Terminal.tsx
TypeScript
1"use client";
2import React, { useState, useEffect, useRef, useCallback } from "react";
3import { Command, Send, Copy, RotateCcw } from "lucide-react";
4
5export type TerminalProps = {
6  bgColor?: string;
7  textColor?: string;
8  command?: string;
9  commandBg?: string;
10  commandMessage?: string;
11  processingSteps?: string[];
12  finalMessage?: string;
13  stepDelay?: number;
14  typingDelay?: number;
15  icon?: React.ReactNode;
16  promptSymbol?: string;
17  inputPlaceholder?: string;
18  outputHeight?: string;
19  rounded?: string;
20  className?: string;
21  autoMode?: boolean;
22  repeat?: boolean;
23  repeatDelay?: number;
24};
25
26const InteractiveTerminal: React.FC<TerminalProps> = ({
27  bgColor = "bg-gray-900",
28  textColor = "text-green-400",
29  command = "help",
30  commandBg = "bg-gray-950",
31  rounded = "sm",
32  commandMessage = "Enter this command:",
33  processingSteps = ["Processing command..."],
34  finalMessage = "Command executed successfully!",
35  stepDelay = 1000,
36  typingDelay = 100,
37  icon = <Command className="mr-2" />,
38  promptSymbol = "$",
39  inputPlaceholder = "Type your command here...",
40  outputHeight = "h-80",
41  autoMode = false,
42  repeat = false,
43  repeatDelay = 3000,
44}) => {
45  const [input, setInput] = useState("");
46  const [output, setOutput] = useState<string[]>([]);
47  const [step, setStep] = useState(0);
48  const [copied, setCopied] = useState(false);
49  const [typing, setTyping] = useState(false);
50  const [charIndex, setCharIndex] = useState(0);
51  const outputRef = useRef<HTMLDivElement>(null);
52  const [commandExecuted, setCommandExecuted] = useState(false);
53  const [completed, setCompleted] = useState(false);
54
55  const resetTerminal = useCallback(() => {
56    setOutput([]);
57    setStep(0);
58    setCharIndex(0);
59    setTyping(false);
60    setCommandExecuted(false);
61    setCompleted(false);
62  }, []);
63
64  const executeCommand = useCallback(() => {
65    setOutput((prev) => [...prev, `${promptSymbol} ${input}`]);
66    setStep(1);
67    setInput("");
68  }, [promptSymbol, input]);
69
70  useEffect(() => {
71    if (outputRef.current) {
72      outputRef.current.scrollTop = outputRef.current.scrollHeight;
73    }
74  }, [output]);
75
76  useEffect(() => {
77    if (autoMode && !typing && !commandExecuted) {
78      const timer = setTimeout(() => {
79        setTyping(true);
80        setCharIndex(0);
81      }, 500);
82      return () => clearTimeout(timer);
83    }
84  }, [autoMode, typing, commandExecuted]);
85
86  useEffect(() => {
87    if (autoMode && repeat && completed) {
88      const repeatTimer = setTimeout(() => {
89        resetTerminal();
90      }, repeatDelay);
91      return () => clearTimeout(repeatTimer);
92    }
93  }, [autoMode, repeat, completed, resetTerminal, repeatDelay]);
94
95  useEffect(() => {
96    if (typing && charIndex < command.length) {
97      const timer = setTimeout(() => {
98        setInput(command.substring(0, charIndex + 1));
99        setCharIndex(charIndex + 1);
100      }, typingDelay);
101      return () => clearTimeout(timer);
102    } else if (typing && charIndex === command.length) {
103      const timer = setTimeout(() => {
104        executeCommand();
105        setTyping(false);
106        setCommandExecuted(true);
107      }, 500);
108      return () => clearTimeout(timer);
109    }
110  }, [typing, charIndex, command, typingDelay, executeCommand]);
111
112  useEffect(() => {
113    if (step > 0 && step <= processingSteps.length) {
114      setOutput((prev) => [...prev, processingSteps[step - 1]]);
115      const timer = setTimeout(() => setStep(step + 1), stepDelay);
116      return () => clearTimeout(timer);
117    } else if (step > processingSteps.length) {
118      setOutput((prev) => [...prev, finalMessage]);
119      setCompleted(true);
120    }
121  }, [step, processingSteps, finalMessage, stepDelay]);
122
123  const handleSubmit = (e: React.FormEvent) => {
124    e.preventDefault();
125    executeCommand();
126    setCommandExecuted(true);
127  };
128
129  const copyCommand = () => {
130    navigator.clipboard.writeText(command);
131    setCopied(true);
132    setTimeout(() => setCopied(false), 2000);
133  };
134
135  const replayCommand = () => {
136    resetTerminal();
137  };
138
139  return (
140    <div
141      className={`max-w-4xl mx-auto p-3 md:p-6 ${bgColor} ${textColor} rounded-${rounded} shadow-lg font-mono scrollbar-thin`}
142    >
143      <div
144        className={`mb-4 p-2 ${commandBg} border-${textColor} rounded flex items-center justify-between`}
145      >
146        <div className="flex items-center gap-1">
147          <div className="-mt-1">{icon}</div>
148          <span>
149            {commandMessage} <strong>{command}</strong>
150          </span>
151        </div>
152        <div className="flex gap-2">
153          {autoMode ? (
154            completed &&
155            !repeat && (
156              <button
157                onClick={replayCommand}
158                className={`px-2 py-1 ${textColor} rounded text-sm flex items-center cursor-pointer`}
159                title="Replay"
160                type="button"
161              >
162                <RotateCcw className="w-4 h-4 mr-1" />
163                Replay
164              </button>
165            )
166          ) : step === 0 ? (
167            <button
168              onClick={copyCommand}
169              className={`px-2 py-1 ${textColor} rounded text-sm flex items-center cursor-pointer`}
170              title="Copy command"
171              type="button"
172            >
173              <Copy className="w-4 h-4 mr-1" />
174              {copied ? "Copied!" : "Copy"}
175            </button>
176          ) : (
177            <button
178              onClick={resetTerminal}
179              className={`px-2 py-1 ${textColor} rounded text-sm flex items-center cursor-pointer`}
180              title="Reset terminal"
181              type="button"
182            >
183              <RotateCcw className="w-4 h-4 mr-1" />
184              Reset
185            </button>
186          )}
187        </div>
188      </div>
189      <div
190        ref={outputRef}
191        className={`${outputHeight} mb-4 p-2 bg-black rounded hide-scrollbar overflow-y-auto`}
192      >
193        {output.map((line, index) => (
194          <pre key={index} className="whitespace-pre-wrap">
195            {line}
196          </pre>
197        ))}
198        {typing && (
199          <pre className="whitespace-pre-wrap cursor-typing">
200            {promptSymbol} {input}
201          </pre>
202        )}
203        {autoMode && repeat && completed && (
204          <pre className="text-gray-500 italic mt-2">
205            Repeating command in {Math.ceil(repeatDelay / 1000)} seconds...
206          </pre>
207        )}
208      </div>
209      {!autoMode && step === 0 && !commandExecuted && (
210        <form onSubmit={handleSubmit} className="flex items-center">
211          <span className="mr-2">{promptSymbol}</span>
212          <input
213            type="text"
214            value={input}
215            onChange={(e) => setInput(e.target.value)}
216            className="flex-grow bg-transparent focus:outline-none cursor-typing"
217            placeholder={inputPlaceholder}
218          />
219          <button
220            type="button"
221            onClick={executeCommand}
222            className="ml-2 p-1 rounded-full hover:bg-gray-700 transition-colors cursor-pointer"
223            title="Send command"
224          >
225            <Send className="w-4 h-4" />
226          </button>
227        </form>
228      )}
229      <style jsx>{`
230        @keyframes blink {
231          0% {
232            opacity: 0;
233          }
234          50% {
235            opacity: 1;
236          }
237          100% {
238            opacity: 0;
239          }
240        }
241        .cursor-typing::after {
242          content: "|";
243          animation: blink 1s infinite;
244        }
245        pre {
246          animation: fadeIn 0.5s ease-in-out;
247        }
248        @keyframes fadeIn {
249          from {
250            opacity: 0;
251          }
252          to {
253            opacity: 1;
254          }
255        }
256        .hide-scrollbar::-webkit-scrollbar {
257          display: none !important;
258        }
259        .hide-scrollbar {
260          -ms-overflow-style: none !important;
261          scrollbar-width: none !important;
262        }
263      `}</style>
264    </div>
265  );
266};
267
268export default InteractiveTerminal;
269
3

Final Steps

Update Import Paths

Make sure to update the import paths in the component code to match your project structure. For example, change @/components/ui/button to match your UI components location.

Props

NameTypeDefaultDescription
autoModebooleanfalseEnables automatic command execution.
bgColorstring"bg-gray-900"Background color for the terminal container.
textColorstring"text-green-400"Text color for the terminal output.
commandstring"help"Command that users need to enter to trigger the terminal sequence.
commandBgstring"bg-gray-950"Background color for the command info bar.
roundedstring"sm"Border radius for the terminal container.
commandMessagestring"Enter this command:"Message to display about the command.
processingStepsstring[]["Processing command..."]Array of processing steps to display sequentially.
finalMessagestring"Command executed successfully!"Final message to display after all processing steps.
stepDelaynumber1000Delay between processing steps in milliseconds.
titlestring"Interactive Terminal"Title displayed above the terminal.
iconReact.ReactNode<TerminalIcon className="mr-2" />Icon to display in the command info bar.
promptSymbolstring"$"Terminal prompt symbol displayed before user input.
inputPlaceholderstring"Type your command here..."Placeholder text for the input field.
outputHeightstring"h-80"Height for the terminal output area.
classNamestring""Custom class names to apply to the container.
backgroundGradientstring"bg-gray-100"Background gradient for the container.

Examples

Copy and paste this command: npm run deploy-love
$

Love Terminal

Spread positivity with charming messages

Run this command to deploy: deploy --production
$

Deployment Terminal

Visualize your CI/CD deployment process

Order your coffee with: brew --coffee latte

Coffee Order Terminal

Showcase products with fun interactions