react-command-flow
v1.6.4
Published
A beautiful interactive CLI-style command interface for React apps
Maintainers
Readme
🖥️ React Command Flow
A beautifully interactive CLI-style command interface for React apps.
Type commands, flow through steps, and build dynamic, conversational UI experiences.
✨ Features
- 🖋 Terminal-style input & output
- 📋 Step-based command flows
- ✅ Built-in validation (sync & async)
- 🔀 Conditional prompts, branching logic
- ⚡ Dynamic suggestions & tab completion
- 💅 Theme support
- 🔍 Help & clear commands out of the box
- 🔌 Easy to extend and customize
📦 Installation
npm install react-command-flow⚡ Usage
import React from "react";
import { CommandLine } from "react-command-flow";
import { commands } from "./commands";
export default function App() {
return (
<div style={{ padding: "2rem" }}>
<h1>React Command Flow Demo</h1>
<CommandLine commands={commands} />
</div>
);
}📚 Defining Commands
const colors = ["Red", "Green", "Blue", "Yellow", "Purple"];
const shapes = ["Circle", "Square", "Triangle", "Rectangle", "Hexagon"];
const commands = {
"color-shape-quiz": {
description: "Start your color and shape quiz 🌈🔺",
steps: [
{
prompt: async () => {
await new Promise((res) => setTimeout(res, 1000)); // async wait
return `What is your favorite color?\n${colors
.map((c, i) => `${i + 1}. ${c}`)
.join("\n")}\nEnter color index:`;
},
field: "colorIndex",
validate: async (val) => {
return ["1", "2", "3", "4", "5"].includes(val)
? true
: "❌ Please select a number between 1 and 5.";
},
redirect: (val) => {
const color = colors[parseInt(val) - 1];
if (color === "Red") return "color-red-flow";
if (color === "Green") return "color-green-flow";
if (color === "Blue") return "color-blue-flow";
if (color === "Yellow") return "color-yellow-flow";
if (color === "Purple") return "color-purple-flow";
return "color-other-flow";
},
},
],
onComplete: () => "Redirecting...",
},
"color-red-flow": {
description: "Red color path 🔴",
hidden: true,
steps: [
{
prompt: "What shape do you associate with Red?",
field: "redShape",
validate: (val) => {
return shapes.includes(val) ? true : "❌ Please enter a valid shape.";
},
},
],
onComplete: async () => {
await new Promise((res) => setTimeout(res, 1000)); // fake async post
return "✅ Red is a powerful color!";
},
},
"color-green-flow": {
description: "Green color path 🟢",
hidden: true,
steps: [
{
prompt: "What shape do you associate with Green?",
field: "greenShape",
validate: (val) => {
return shapes.includes(val) ? true : "❌ Please enter a valid shape.";
},
},
],
onComplete: async () => {
await new Promise((res) => setTimeout(res, 1000)); // fake async post
return "✅ Green is a calming color!";
},
},
"color-blue-flow": {
description: "Blue color path 🔵",
hidden: true,
steps: [
{
prompt: "What shape do you associate with Blue?",
field: "blueShape",
validate: (val) => {
return shapes.includes(val) ? true : "❌ Please enter a valid shape.";
},
},
],
onComplete: async () => {
await new Promise((res) => setTimeout(res, 1000)); // fake async post
return "✅ Blue is a soothing color!";
},
},
"color-yellow-flow": {
description: "Yellow color path 🟡",
hidden: true,
steps: [
{
prompt: "What shape do you associate with Yellow?",
field: "yellowShape",
validate: (val) => {
return shapes.includes(val) ? true : "❌ Please enter a valid shape.";
},
},
{
prompt: "What emotion do you associate with Yellow?",
field: "yellowEmotion",
validate: (val) => {
const emotions = ["happiness", "energy", "warmth", "caution"];
return emotions.includes(val.toLowerCase())
? true
: "❌ Please enter a valid emotion (happiness, energy, warmth, caution).";
},
},
{
prompt: "What object do you associate with Yellow?",
field: "yellowObject",
validate: (val) => {
return val.length > 0 ? true : "❌ Please enter a valid object.";
},
},
],
onComplete: async () => {
await new Promise((res) => setTimeout(res, 1000)); // fake async post
return "✅ Yellow is a bright and cheerful color!";
},
},
"color-purple-flow": {
description: "Purple color path 🟣",
hidden: true,
steps: [
{
prompt: "What shape do you associate with Purple?",
field: "purpleShape",
validate: (val) => {
return shapes.includes(val) ? true : "❌ Please enter a valid shape.";
},
},
],
onComplete: async () => {
await new Promise((res) => setTimeout(res, 1000)); // fake async post
return "✅ Purple is a royal color!";
},
},
"color-other-flow": {
description: "Other color path 🌈",
hidden: true,
steps: [],
onComplete: () => "💛 All colors are beautiful!",
},
help: {
description: "List available commands",
steps: [],
onComplete: (_, __, all) => {
return Object.entries(all)
.filter(([_, c]) => !c.hidden)
.map(([name, c]) => `- ${name}: ${c.description}`)
.join("\n");
},
},
clear: {
description: "Clear the screen",
steps: [],
onComplete: () => "__CLEAR__",
},
};
// Alias added outside
commands["?"] = commands.help;
export default commands;🧠 Advanced Examples
- 🔁 Async prompt resolution
- ✅ Custom validation per step
- 🔀
onSteplogic to transform values - 🧭 Conditional steps (based on previous answers)
- 📦 Redirects to subcommands (optional)
- 🔄 Repeat functionality with custom repeat questions
🎯 Repeat Configuration
Commands can use a comprehensive repeat object for full control over the repeat behavior:
const commands = {
"job-application": {
description: "Job application flow",
steps: [
/* ... */
],
onComplete: (data) => `Application submitted for ${data.name}`,
repeat: {
mode: "ask",
question: "💼 Submit another application ({{yes}},{{no}})?",
yes: "sure",
no: "nope",
wrongInput: "Please select either {{yes}} or {{no}}:",
yesReply: "Starting new application...",
noReply: "Application process completed.",
targetCommand: "other-command", // Optional: redirect to different command
},
},
survey: {
description: "User survey",
steps: [
/* ... */
],
onComplete: (data) => "Survey completed!",
repeat: {
mode: "ask",
// Function-based custom repeat question
question: ({ yesOption, noOption, commandName }) =>
`🔄 Take the survey again? Type '${yesOption}' to continue or '${noOption}' to finish.`,
yes: "y",
no: "n",
wrongInput: "Please select either {{yes}} or {{no}}:",
yesReply: "Let's do another survey!",
noReply: "Thanks for participating!",
},
},
"async-demo": {
description: "Async repeat question demo",
steps: [
/* ... */
],
onComplete: (data) => "Task completed!",
repeat: {
mode: "ask",
// Async function-based custom repeat question
question: async ({ yesOption, noOption, commandName }) => {
// Simulate async operation (e.g., checking user preferences)
await new Promise((resolve) => setTimeout(resolve, 500));
return `🤔 Continue with another task? Type '${yesOption}' or '${noOption}'.`;
},
yes: "y",
no: "n",
wrongInput: "Please select either {{yes}} or {{no}}:",
yesReply: "Great! Let's continue...",
noReply: "Task session ended.",
},
},
};Repeat Object Properties:
mode- "yes", "no", or "ask"question- Custom repeat question (string or function)yes- Custom "yes" option textno- Custom "no" option textwrongInput- Custom error message for invalid inputyesReply- Custom message when user selects "yes"noReply- Custom message when user selects "no"targetCommand- Optional: different command to run on repeat
Available placeholders for string-based questions and messages:
{{yes}}- The "yes" option value{{no}}- The "no" option value{command}- The command name (legacy format)
Function parameters for dynamic questions:
yesOption- The "yes" option stringnoOption- The "no" option stringcommandName- The name of the command
🖌️ Theming
Supports custom themes with colors and fonts.
You can pass a theme prop to <CommandLine />.
💡 Example Commands You Can Try
helloprofile-setupselect-serverhelpclear
📜 License
MIT — free to use, modify, contribute.
Made with 💛 by Stam & React Command Flow Contributors.
⭐ Star this repo if you like it!
Let's build the coolest CLI UIs in the React world 🌍
