Architecture
Prompt Steps

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:

Extend React Kickstart:

User-Centric Design: The prompt system prioritizes user experience with intelligent defaults, helpful descriptions, and conditional navigation.