Clack Guidelines
Overview
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
bash
1npm install @clack/prompts
Basic Program
javascript
1import { text, isCancel, cancel } from "@clack/prompts";
2
3const projectPath = await text({
4 message: "Where should we create your project?",
5 placeholder: "./my-awesome-project",
6 initialValue: "./sparkling-solid",
7 validate: (value) => {
8 if (!value) return "Please enter a path.";
9 if (value[0] !== ".") return "Please enter a relative path.";
10 },
11});
12
13if (isCancel(projectPath)) {
14 cancel("Operation cancelled.");
15 process.exit(0);
16}
17
18console.log(`Creating project at: ${projectPath}`);
Complete Interactive CLI
javascript
1import * as p from "@clack/prompts";
2import color from "picocolors";
3
4async function createApp() {
5 console.clear();
6 p.intro(color.bgCyan(color.black(" create-myapp ")));
7
8 const config = await p.group(
9 {
10 name: () =>
11 p.text({
12 message: "What is your project name?",
13 placeholder: "my-awesome-app",
14 validate: (value) => {
15 if (!value) return "Project name is required";
16 },
17 }),
18
19 framework: () =>
20 p.select({
21 message: "Choose your framework",
22 options: [
23 { value: "react", label: "React", hint: "Popular" },
24 { value: "vue", label: "Vue" },
25 { value: "svelte", label: "Svelte" },
26 ],
27 }),
28
29 typescript: () =>
30 p.confirm({
31 message: "Use TypeScript?",
32 initialValue: true,
33 }),
34 },
35 {
36 onCancel: () => {
37 p.cancel("Setup cancelled");
38 process.exit(0);
39 },
40 },
41 );
42
43 console.log(config);
44}
45
46createApp().catch(console.error);
Common Patterns
Text Input
Single-line text input with validation and placeholders.
javascript
1import { text } from "@clack/prompts";
2
3const name = await text({
4 message: "Enter your name",
5 placeholder: "John Doe",
6 validate: (value) => {
7 if (!value) return "Name is required";
8 },
9});
Secure password input with masking.
javascript
1import { password } from "@clack/prompts";
2
3const userPassword = await password({
4 message: "Provide a password",
5 mask: "•",
6 validate: (value) => {
7 if (!value) return "Please enter a password.";
8 if (value.length < 8) return "Password should have at least 8 characters.";
9 },
10});
Autocomplete Single Selection
Type-ahead search with real-time filtering.
javascript
1import { autocomplete } from "@clack/prompts";
2
3const country = await autocomplete({
4 message: "Select a country",
5 options: [
6 { value: "us", label: "United States", hint: "NA" },
7 { value: "ca", label: "Canada", hint: "NA" },
8 { value: "uk", label: "United Kingdom", hint: "EU" },
9 ],
10 placeholder: "Type to search countries...",
11 maxItems: 8,
12 initialValue: "us",
13});
Autocomplete Multi-Select
Type-ahead search with multi-selection.
javascript
1import { autocompleteMultiselect } from "@clack/prompts";
2
3const frameworks = await autocompleteMultiselect({
4 message: "Select frameworks (type to filter)",
5 options: [
6 { value: "react", label: "React", hint: "Frontend/UI" },
7 { value: "vue", label: "Vue.js", hint: "Frontend/UI" },
8 { value: "nextjs", label: "Next.js", hint: "React Framework" },
9 ],
10 placeholder: "Type to filter...",
11 maxItems: 8,
12 initialValues: ["react", "nextjs"],
13 required: true,
14});
Choose one option from a scrollable list.
javascript
1import { select } from "@clack/prompts";
2
3const projectType = await select({
4 message: "Pick a project type",
5 initialValue: "ts",
6 maxItems: 5,
7 options: [
8 { value: "ts", label: "TypeScript" },
9 { value: "js", label: "JavaScript" },
10 { value: "rust", label: "Rust" },
11 { value: "go", label: "Go" },
12 ],
13});
Select multiple options from a list.
javascript
1import { multiselect } from "@clack/prompts";
2
3const tools = await multiselect({
4 message: "Select additional tools",
5 initialValues: ["prettier", "eslint"],
6 required: true,
7 options: [
8 { value: "prettier", label: "Prettier", hint: "recommended" },
9 { value: "eslint", label: "ESLint", hint: "recommended" },
10 { value: "stylelint", label: "Stylelint" },
11 ],
12});
Confirmation Dialog
Binary yes/no choice.
javascript
1import { confirm } from "@clack/prompts";
2
3const shouldInstallDeps = await confirm({
4 message: "Install dependencies?",
5 active: "Yes, please",
6 inactive: "No, skip",
7 initialValue: false,
8});
Progress Bar
Visual progress indicator for long-running operations.
javascript
1import { progress } from "@clack/prompts";
2import { setTimeout } from "node:timers/promises";
3
4const downloadProgress = progress({
5 style: "block",
6 max: 100,
7 size: 40,
8});
9
10downloadProgress.start("Downloading packages");
11
12for (let i = 0; i < 100; i += 10) {
13 await setTimeout(500);
14 downloadProgress.advance(10);
15 downloadProgress.message(`Downloaded ${i + 10}%`);
16}
17
18downloadProgress.stop("Download complete!");
Spinner Loading Indicator
Display progress for long-running operations.
javascript
1import { spinner } from "@clack/prompts";
2import { setTimeout } from "node:timers/promises";
3
4const s = spinner();
5
6s.start("Installing packages via pnpm");
7await setTimeout(2000);
8
9s.message("Downloading dependencies");
10await setTimeout(1500);
11
12s.stop("Installation complete!");
Task Log with Live Output
Display streaming output that clears on success and persists on error.
javascript
1import { taskLog } from "@clack/prompts";
2import { spawn } from "node:child_process";
3
4const log = taskLog({
5 title: "Running npm install",
6 limit: 10,
7 retainLog: true,
8 spacing: 1,
9});
10
11const npmInstall = spawn("npm", ["install"]);
12
13npmInstall.stdout.on("data", (data) => {
14 log.message(data.toString(), { raw: true });
15});
16
17npmInstall.on("close", (code) => {
18 if (code === 0) {
19 log.success("Installation complete!");
20 } else {
21 log.error("Installation failed!", { showLog: true });
22 }
23});
Streaming Output for LLMs
Stream dynamic content from async iterables.
javascript
1import { stream } from "@clack/prompts";
2import { setTimeout } from "node:timers/promises";
3
4await stream.step(
5 (async function* () {
6 const words = "Building your application with modern tools".split(" ");
7 for (const word of words) {
8 yield word;
9 yield " ";
10 await setTimeout(100);
11 }
12 })(),
13);
Grouped Prompts with Sequential Execution
Execute multiple prompts in sequence with shared state.
javascript
1import * as p from "@clack/prompts";
2import color from "picocolors";
3
4p.intro(color.bgCyan(color.black(" create-app ")));
5
6const project = await p.group(
7 {
8 path: () =>
9 p.text({
10 message: "Where should we create your project?",
11 placeholder: "./sparkling-solid",
12 validate: (value) => {
13 if (!value) return "Please enter a path.";
14 if (value[0] !== ".") return "Please enter a relative path.";
15 },
16 }),
17
18 type: ({ results }) =>
19 p.select({
20 message: `Pick a project type within "${results.path}"`,
21 initialValue: "ts",
22 options: [
23 { value: "ts", label: "TypeScript" },
24 { value: "js", label: "JavaScript" },
25 { value: "rust", label: "Rust" },
26 ],
27 }),
28
29 tools: () =>
30 p.multiselect({
31 message: "Select additional tools",
32 options: [
33 { value: "prettier", label: "Prettier", hint: "recommended" },
34 { value: "eslint", label: "ESLint", hint: "recommended" },
35 ],
36 }),
37
38 install: () =>
39 p.confirm({
40 message: "Install dependencies?",
41 initialValue: true,
42 }),
43 },
44 {
45 onCancel: () => {
46 p.cancel("Operation cancelled.");
47 process.exit(0);
48 },
49 },
50);
51
52console.log(project.path, project.type, project.tools, project.install);
Task Execution with Spinners
Run multiple tasks with automatic spinner management.
javascript
1import * as p from "@clack/prompts";
2import { setTimeout } from "node:timers/promises";
3
4await p.tasks([
5 {
6 title: "Installing dependencies",
7 task: async (message) => {
8 message("Downloading packages");
9 await setTimeout(1000);
10 message("Building native modules");
11 await setTimeout(500);
12 return "Installed 127 packages";
13 },
14 },
15 {
16 title: "Running linter",
17 task: async () => {
18 // Run linter
19 return "No issues found";
20 },
21 },
22]);
Logging Messages
Display formatted status messages with different severity levels.
javascript
1import { log } from "@clack/prompts";
2import color from "picocolors";
3
4log.info("Starting build process...");
5log.step("Compiling TypeScript");
6log.success("Build completed successfully!");
7log.warn("Some dependencies are outdated");
8log.error("Failed to connect to database");
9
10// Custom symbol
11log.message("Custom message", {
12 symbol: color.cyan("→"),
13});
Intro, Outro, and Notes
Frame your CLI sessions with beautiful headers.
javascript
1import { intro, outro, note } from "@clack/prompts";
2import color from "picocolors";
3
4intro(color.bgCyan(color.black(" my-cli-tool v2.0.0 ")));
5
6// ... prompts and operations ...
7
8note("cd my-project\nnpm install\nnpm run dev", "Next steps");
9
10outro(
11 `Problems? ${color.underline(color.cyan("https://github.com/myorg/myproject/issues"))}`,
12);
Custom Key Bindings
Configure alternative key mappings for navigation.
javascript
1import { updateSettings } from "@clack/prompts";
2
3// Enable WASD navigation
4updateSettings({
5 aliases: {
6 w: "up",
7 s: "down",
8 a: "left",
9 d: "right",
10 },
11});
Non-Interactive Mode Detection
Handle CI environments where prompts aren't supported.
javascript
1import { text, block } from "@clack/prompts";
2
3// In CI environments, prompts automatically use defaults
4const name = await text({
5 message: "Project name?",
6 defaultValue: "default-project",
7});
8
9// block() utility prevents input in non-TTY environments
10const unblock = block();
11// Perform operations
12unblock();
Cancellation and Error Handling
Robust error handling for user cancellations and validation errors.
javascript
1import * as p from "@clack/prompts";
2
3async function setupProject() {
4 try {
5 const responses = await p.group({
6 name: () =>
7 p.text({
8 message: "Project name?",
9 validate: (v) => {
10 if (!v) return "Required";
11 },
12 }),
13
14 confirm: ({ results }) =>
15 p.confirm({
16 message: `Create "${results.name}"?`,
17 }),
18 });
19
20 // Check if any step was cancelled
21 if (p.isCancel(responses.name) || p.isCancel(responses.confirm)) {
22 p.cancel("Setup cancelled");
23 return;
24 }
25
26 if (!responses.confirm) {
27 p.outro("Setup aborted");
28 return;
29 }
30
31 // Proceed with setup
32 p.outro("Project created!");
33 } catch (error) {
34 p.log.error(`Setup failed: ${error.message}`);
35 process.exit(1);
36 }
37}
38
39setupProject();
Advanced Features
Custom Render Function with @clack/core
Build custom prompts with full control over rendering.
javascript
1import { TextPrompt } from "@clack/core";
2import color from "picocolors";
3
4const customPrompt = new TextPrompt({
5 validate: (value) => (value.length < 3 ? "Too short!" : undefined),
6 render() {
7 const title = `>>> ${color.bold("Enter your name")}:`;
8 const input = this.valueWithCursor || color.dim("(empty)");
9
10 switch (this.state) {
11 case "error":
12 return `${title}\n${color.red(input)}\n${color.red(this.error)}`;
13 case "submit":
14 return `${title} ${color.green(this.value)}`;
15 case "cancel":
16 return `${title} ${color.strikethrough(this.value)}`;
17 default:
18 return `${title}\n${color.cyan(input)}`;
19 }
20 },
21});
22
23const result = await customPrompt.prompt();
24console.log(`Result: ${result}`);
Event-Driven Custom Prompt
Subscribe to prompt events for advanced interactions.
javascript
1import { TextPrompt } from "@clack/core";
2
3const prompt = new TextPrompt({
4 render() {
5 return `Input: ${this.valueWithCursor}`;
6 },
7});
8
9// Listen to events
10prompt.on("value", (value) => {
11 console.log(`Current value: ${value}`);
12});
13
14prompt.on("submit", () => {
15 console.log("User submitted the form");
16});
17
18prompt.on("cancel", () => {
19 console.log("User cancelled");
20});
21
22const result = await prompt.prompt();
Complete CLI Application Example
Full-featured setup wizard combining multiple prompt types.
javascript
1import { setTimeout } from "node:timers/promises";
2import * as p from "@clack/prompts";
3import color from "picocolors";
4
5async function createApp() {
6 console.clear();
7
8 p.intro(color.bgCyan(color.black(" create-myapp ")));
9
10 const config = await p.group(
11 {
12 name: () =>
13 p.text({
14 message: "What is your project name?",
15 placeholder: "my-awesome-app",
16 validate: (value) => {
17 if (!value) return "Project name is required";
18 if (!/^[a-z0-9-]+$/.test(value)) {
19 return "Use only lowercase letters, numbers, and hyphens";
20 }
21 },
22 }),
23
24 framework: () =>
25 p.select({
26 message: "Choose your framework",
27 options: [
28 { value: "react", label: "React", hint: "Popular" },
29 { value: "vue", label: "Vue" },
30 { value: "svelte", label: "Svelte" },
31 { value: "solid", label: "Solid" },
32 ],
33 }),
34
35 features: () =>
36 p.multiselect({
37 message: "Select features",
38 required: false,
39 options: [
40 { value: "router", label: "Router" },
41 { value: "state", label: "State Management" },
42 { value: "testing", label: "Testing Setup" },
43 { value: "ci", label: "CI/CD Pipeline" },
44 ],
45 }),
46
47 typescript: () =>
48 p.confirm({
49 message: "Use TypeScript?",
50 initialValue: true,
51 }),
52
53 install: () =>
54 p.confirm({
55 message: "Install dependencies now?",
56 initialValue: true,
57 }),
58 },
59 {
60 onCancel: () => {
61 p.cancel("Setup cancelled");
62 process.exit(0);
63 },
64 },
65 );
66
67 // Execute setup tasks
68 await p.tasks([
69 {
70 title: "Creating project structure",
71 task: async () => {
72 // Create project files
73 return `Created ${config.name}`;
74 },
75 },
76 {
77 title: "Installing dependencies",
78 task: async (message) => {
79 message("Resolving packages...");
80 await setTimeout(1000);
81 message("Downloading...");
82 await setTimeout(2000);
83 return "Installed 42 packages";
84 },
85 enabled: config.install,
86 },
87 ]);
88
89 // Display next steps
90 const nextSteps = [
91 `cd ${config.name}`,
92 config.install ? "" : "npm install",
93 "npm run dev",
94 ]
95 .filter(Boolean)
96 .join("\n");
97
98 p.note(nextSteps, "Next steps");
99
100 p.outro(
101 `All set! ${color.underline(color.cyan("https://docs.example.com"))}`,
102 );
103}
104
105async function createProjectFiles(config) {
106 // Implementation...
107}
108
109createApp().catch(console.error);
Detailed API Reference
For comprehensive API documentation including all prompt types, configuration options, and advanced features, see the detailed API reference:
references/clack-api.md
This reference includes:
- Complete prompt type documentation
- Configuration options for each prompt
- Validation and error handling patterns
- Streaming and async patterns
- Custom key bindings and settings
- Integration with @clack/core
- TypeScript support
- And more
Resources
references/
This skill includes detailed API documentation:
- clack-api.md - Comprehensive API reference for Clack prompts, organized by prompt type with code examples for each feature.