Prompt Steps
The prompt step system handles interactive user input collection during project setup. Each step focuses on a specific aspect of project configuration, presenting choices and validating input.
๐ฏ Prompt Architecture
๐ Step Structure
๐๏ธ Framework Step
Choose between Vite and Next.js
๐ Language Step
TypeScript vs JavaScript selection
๐จ Styling Step
CSS framework and styling options
๐๏ธ State Step
State management library choice
๐ API Step
HTTP client and data fetching setup
๐งช Testing Step
Testing framework selection
๐งญ Routing Step
Client-side routing configuration
๐ Deployment Step
Deployment platform options
โ๏ธ Tool Steps
Package manager, Git, editor, linting
๐๏ธ Base Step Architecture
All prompt steps extend the BaseStep
class for consistency:
// Base step implementation
class BaseStep {
constructor(name, message) {
this.name = name;
this.message = message;
}
// Abstract methods - must be implemented by subclasses
getChoices(previousAnswers) {
throw new Error("getChoices must be implemented");
}
getNextStep(answer, previousAnswers) {
return null; // Override if step has conditional navigation
}
// Validation and formatting
validate(answer) {
return true; // Override for custom validation
}
format(answer) {
return answer; // Override for custom formatting
}
// Skip conditions
shouldSkip(previousAnswers) {
return false; // Override to conditionally skip step
}
}
๐ฏ Key Prompt Steps
Framework Selection Step
class FrameworkStep extends BaseStep {
constructor() {
super('framework', 'Which framework would you like to use?')
}
getChoices() {
return [
{
name: 'Vite (Fast development with instant HMR)',
value: 'vite',
short: 'Vite'
},
{
name: 'Next.js (Full-stack React framework)',
value: 'nextjs',
short: 'Next.js'
}
]
}
getNextStep(answer) {
// Conditional navigation based on framework choice
if (answer === 'nextjs') {
return 'nextjs-routing'
}
return 'language'
}
}
Styling Selection Step
class StylingStep extends BaseStep {
constructor() {
super('styling', 'How would you like to style your components?')
}
getChoices(previousAnswers) {
const baseChoices = [
{
name: 'Tailwind CSS (Utility-first CSS framework)',
value: 'tailwind',
short: 'Tailwind'
},
{
name: 'styled-components (CSS-in-JS)',
value: 'styled-components',
short: 'styled-components'
},
{
name: 'CSS (Plain CSS files)',
value: 'css',
short: 'CSS'
}
]
// Framework-specific adjustments
if (previousAnswers.framework === 'nextjs') {
// Add Next.js specific styling notes
baseChoices[0].name += ' (Recommended for Next.js)'
}
return baseChoices
}
validate(answer) {
const validChoices = ['tailwind', 'styled-components', 'css']
return validChoices.includes(answer)
}
}
State Management Step
class StateManagementStep extends BaseStep {
constructor() {
super('state', 'Would you like to add state management?')
}
getChoices(previousAnswers) {
return [
{
name: 'Redux Toolkit (Powerful state management with DevTools)',
value: 'redux',
short: 'Redux'
},
{
name: 'Zustand (Lightweight state management)',
value: 'zustand',
short: 'Zustand'
},
{
name: 'None (Use React\'s built-in state)',
value: 'none',
short: 'None'
}
]
}
getRecommendation(previousAnswers) {
// Provide intelligent recommendations
if (previousAnswers.api && previousAnswers.api.includes('react-query')) {
return {
choice: 'zustand',
reason: 'Zustand pairs well with React Query for server state'
}
}
if (previousAnswers.framework === 'nextjs') {
return {
choice: 'redux',
reason: 'Redux Toolkit works great with Next.js SSR'
}
}
return null
}
}
๐ Conditional Navigation
The prompt system supports complex conditional navigation:
Framework-Specific Routing
// Framework step determines routing options
class FrameworkStep extends BaseStep {
getNextStep(answer) {
switch (answer) {
case "nextjs":
return "nextjs-routing"; // Next.js specific routing options
case "vite":
return "language"; // Skip to language, routing comes later
default:
return "language";
}
}
}
// Later in the flow, Vite gets routing options
class RoutingStep extends BaseStep {
shouldSkip(previousAnswers) {
// Only show for Vite projects
return previousAnswers.framework !== "vite";
}
getChoices() {
return [
{
name: "React Router (Client-side routing)",
value: "react-router",
},
{
name: "None (Single page application)",
value: "none",
},
];
}
}
Feature Dependencies
// API step influences testing recommendations
class TestingStep extends BaseStep {
getChoices(previousAnswers) {
const choices = [
{
name: "Vitest (Fast, modern testing framework)",
value: "vitest",
},
{
name: "Jest (Traditional, mature testing framework)",
value: "jest",
},
{
name: "None (No testing setup)",
value: "none",
},
];
// Add framework-specific recommendations
if (previousAnswers.framework === "vite") {
choices[0].name += " (Recommended for Vite)";
}
if (previousAnswers.framework === "nextjs") {
choices[1].name += " (Recommended for Next.js)";
}
return choices;
}
}
๐จ User Experience Features
Smart Defaults
Intelligent Defaults: The prompt system provides smart defaults based on popular combinations and best practices.
class PromptStep extends BaseStep {
getDefault(previousAnswers) {
// Provide intelligent defaults based on previous choices
if (this.name === "typescript") {
return true; // TypeScript is recommended by default
}
if (this.name === "styling" && previousAnswers.framework === "nextjs") {
return "tailwind"; // Tailwind is great with Next.js
}
if (this.name === "testing" && previousAnswers.framework === "vite") {
return "vitest"; // Vitest is optimal for Vite
}
return null;
}
}
Help Text and Descriptions
class StylingStep extends BaseStep {
getHelpText() {
return `
Styling Options:
โข Tailwind CSS: Utility-first CSS framework
- Rapid development with utility classes
- Consistent design system
- Excellent performance with purging
โข styled-components: CSS-in-JS solution
- Component-scoped styles
- Dynamic styling with props
- Theme support
โข CSS: Traditional CSS files
- Full control over styling
- Familiar workflow
- CSS Modules support
`;
}
getChoiceDescription(choice) {
const descriptions = {
tailwind: "Build rapidly with utility classes",
"styled-components": "Component-scoped CSS-in-JS",
css: "Traditional CSS with full control",
};
return descriptions[choice] || "";
}
}
๐งช Step Validation
Input Validation
class BaseStep {
validate(answer, previousAnswers) {
// Basic validation
if (!answer) {
throw new Error("Answer is required");
}
// Choice validation
const validChoices = this.getChoices(previousAnswers).map((c) => c.value);
if (!validChoices.includes(answer)) {
throw new Error(
`Invalid choice: ${answer}. Valid options: ${validChoices.join(", ")}`
);
}
// Custom validation
return this.customValidate(answer, previousAnswers);
}
customValidate(answer, previousAnswers) {
// Override in subclasses for specific validation
return true;
}
}
Cross-Step Validation
// Validate combinations across steps
class PromptValidator {
validateCombination(answers) {
// Check for incompatible combinations
if (answers.framework === "vite" && answers.routing === "app") {
throw new Error("App Router is only available with Next.js");
}
if (answers.state === "redux" && answers.api === "none") {
console.warn("Consider using RTK Query with Redux for API calls");
}
// Validate deployment compatibility
if (answers.deployment === "vercel" && answers.framework === "vite") {
console.info("Vite projects deploy great on Vercel with zero config");
}
return true;
}
}
๐ง Adding New Prompt Steps
Create Step Class
// src/prompts/steps/your-feature-step.js
import { BaseStep } from "./base-step.js";
export class YourFeatureStep extends BaseStep {
constructor() {
super("yourFeature", "Would you like to add your feature?");
}
getChoices(previousAnswers) {
return [
{
name: "Option 1 (Description)",
value: "option1",
short: "Option 1",
},
{
name: "Option 2 (Description)",
value: "option2",
short: "Option 2",
},
{
name: "None",
value: "none",
short: "None",
},
];
}
shouldSkip(previousAnswers) {
// Skip if not relevant to chosen framework
return previousAnswers.framework !== "target-framework";
}
}
Register in Prompt Flow
// src/prompts/prompt-flow.js
import { YourFeatureStep } from "./steps/your-feature-step.js";
const steps = [
new FrameworkStep(),
new LanguageStep(),
new StylingStep(),
new YourFeatureStep(), // Add your step
// ... other steps
];
Handle in Generator
// In your generator
if (userChoices.yourFeature !== "none") {
await this.setupYourFeature(projectPath, userChoices);
}
๐ Next Steps
Explore Related Systems:
- Generators โ - How prompts drive project generation
- Features โ - Feature integration based on prompt choices
- Templates โ - Code generation from user selections
Extend React Kickstart:
- Adding Features โ - Add new prompt steps and features
- Contributing โ - How to contribute to React Kickstart
User-Centric Design: The prompt system prioritizes user experience with intelligent defaults, helpful descriptions, and conditional navigation.