Comprehensive guide for building beautiful interactive command-line interfaces using Clack. Use when creating CLI tools with text input, selections, autocomplete, progress tracking, and streaming output.
This skill provides guidance for building beautiful interactive command-line interfaces using Clack. It covers common patterns for text input, selections, autocomplete, progress tracking, streaming output, and creating complete interactive CLI applications.
Quick Start
Installation
npm install @clack/prompts
Basic Program
import { text, isCancel, cancel } from "@clack/prompts";
const projectPath = await text({
message: "Where should we create your project?",
placeholder: "./my-awesome-project",
initialValue: "./sparkling-solid",
validate: (value) => {
if (!value) return "Please enter a path.";
if (value[0] !== ".") return "Please enter a relative path.";
},
});
if (isCancel(projectPath)) {
cancel("Operation cancelled.");
process.exit(0);
}
console.log(`Creating project at: ${projectPath}`);
Complete Interactive CLI
import * as p from "@clack/prompts";
import color from "picocolors";
async function createApp() {
console.clear();
p.intro(color.bgCyan(color.black(" create-myapp ")));
const config = await p.group(
{
name: () =>
p.text({
message: "What is your project name?",
placeholder: "my-awesome-app",
validate: (value) => {
if (!value) return "Project name is required";
},
}),
framework: () =>
p.select({
message: "Choose your framework",
options: [
{ value: "react", label: "React", hint: "Popular" },
{ value: "vue", label: "Vue" },
{ value: "svelte", label: "Svelte" },
],
}),
typescript: () =>
p.confirm({
message: "Use TypeScript?",
initialValue: true,
}),
},
{
onCancel: () => {
p.cancel("Setup cancelled");
process.exit(0);
},
},
);
console.log(config);
}
createApp().catch(console.error);
Common Patterns
Text Input
Single-line text input with validation and placeholders.
import { text } from "@clack/prompts";
const name = await text({
message: "Enter your name",
placeholder: "John Doe",
validate: (value) => {
if (!value) return "Name is required";
},
});
Password Input
Secure password input with masking.
import { password } from "@clack/prompts";
const userPassword = await password({
message: "Provide a password",
mask: "•",
validate: (value) => {
if (!value) return "Please enter a password.";
if (value.length < 8) return "Password should have at least 8 characters.";
},
});
import { stream } from "@clack/prompts";
import { setTimeout } from "node:timers/promises";
await stream.step(
(async function* () {
const words = "Building your application with modern tools".split(" ");
for (const word of words) {
yield word;
yield " ";
await setTimeout(100);
}
})(),
);
Grouped Prompts with Sequential Execution
Execute multiple prompts in sequence with shared state.
import * as p from "@clack/prompts";
import color from "picocolors";
p.intro(color.bgCyan(color.black(" create-app ")));
const project = await p.group(
{
path: () =>
p.text({
message: "Where should we create your project?",
placeholder: "./sparkling-solid",
validate: (value) => {
if (!value) return "Please enter a path.";
if (value[0] !== ".") return "Please enter a relative path.";
},
}),
type: ({ results }) =>
p.select({
message: `Pick a project type within "${results.path}"`,
initialValue: "ts",
options: [
{ value: "ts", label: "TypeScript" },
{ value: "js", label: "JavaScript" },
{ value: "rust", label: "Rust" },
],
}),
tools: () =>
p.multiselect({
message: "Select additional tools",
options: [
{ value: "prettier", label: "Prettier", hint: "recommended" },
{ value: "eslint", label: "ESLint", hint: "recommended" },
],
}),
install: () =>
p.confirm({
message: "Install dependencies?",
initialValue: true,
}),
},
{
onCancel: () => {
p.cancel("Operation cancelled.");
process.exit(0);
},
},
);
console.log(project.path, project.type, project.tools, project.install);
Task Execution with Spinners
Run multiple tasks with automatic spinner management.
import * as p from "@clack/prompts";
import { setTimeout } from "node:timers/promises";
await p.tasks([
{
title: "Installing dependencies",
task: async (message) => {
message("Downloading packages");
await setTimeout(1000);
message("Building native modules");
await setTimeout(500);
return "Installed 127 packages";
},
},
{
title: "Running linter",
task: async () => {
// Run linter
return "No issues found";
},
},
]);
Logging Messages
Display formatted status messages with different severity levels.
import { log } from "@clack/prompts";
import color from "picocolors";
log.info("Starting build process...");
log.step("Compiling TypeScript");
log.success("Build completed successfully!");
log.warn("Some dependencies are outdated");
log.error("Failed to connect to database");
// Custom symbol
log.message("Custom message", {
symbol: color.cyan("→"),
});
Intro, Outro, and Notes
Frame your CLI sessions with beautiful headers.
import { intro, outro, note } from "@clack/prompts";
import color from "picocolors";
intro(color.bgCyan(color.black(" my-cli-tool v2.0.0 ")));
// ... prompts and operations ...
note("cd my-project\nnpm install\nnpm run dev", "Next steps");
outro(
`Problems? ${color.underline(color.cyan("https://github.com/myorg/myproject/issues"))}`,
);
Custom Key Bindings
Configure alternative key mappings for navigation.