# How to Scaffold Vite + React + TypeScript Projects for Presentations

> Learn to scaffold Vite React TypeScript projects for presentations using the ConardLi/garden-skills starter. Enjoy instant HMR and arrow-key navigation.

- Repository: [ConardLi/garden-skills](https://github.com/ConardLi/garden-skills)
- Tags: tutorial
- Published: 2026-08-31

---

**The ConardLi/garden-skills repository provides a production-ready starter at `website/web-design-website` that combines Vite's instant HMR with a step-driven presentation engine featuring arrow-key navigation and per-slide theming.**

When you scaffold Vite React TypeScript projects for presentations, configuring routing, state management, and keyboard controls from scratch consumes time better spent on content. The **garden-skills** reference implementation eliminates this overhead by shipping a zero-config slide system built on React 19 and Vite 8. It uses a minimal Zustand-like store to manage linear chapter progression, allowing you to focus on component design while the framework handles navigation, progress tracking, and theme switching.

## Repository Architecture and Key Components

The starter isolates presentation logic into three functional layers: configuration, state management, and rendering. This separation allows you to modify themes or navigation behavior without touching Vite's build pipeline.

### Vite and TypeScript Configuration

The build toolchain resides in the repository root under `website/web-design-website`. The **[`vite.config.ts`](https://github.com/ConardLi/garden-skills/blob/main/vite.config.ts)** file registers the official `@vitejs/plugin-react` plugin to enable Fast Refresh during development. TypeScript safety is enforced through project references: **[`tsconfig.app.json`](https://github.com/ConardLi/garden-skills/blob/main/tsconfig.app.json)** handles the React application context, while **[`tsconfig.node.json`](https://github.com/ConardLi/garden-skills/blob/main/tsconfig.node.json)** isolates Vite's configuration types. The root **[`tsconfig.json`](https://github.com/ConardLi/garden-skills/blob/main/tsconfig.json)** orchestrates these references to prevent type leakage between Node tooling and browser code.

### The Step Store and Navigation Engine

Linear presentation flow is governed by **[`src/store/useStep.ts`](https://github.com/ConardLi/garden-skills/blob/main/src/store/useStep.ts)**, a lightweight store that tracks the current chapter index and exposes `stepStore.next()` and `stepStore.prev()` methods. Unlike heavy routing libraries, this store implements a simple integer counter that maps directly to an array of slide components, eliminating bundle bloat.

Keyboard navigation is wired via **[`src/stage/useHotKeys.ts`](https://github.com/ConardLi/garden-skills/blob/main/src/stage/useHotKeys.ts)**, which attaches a `keydown` listener to the window object. When users press the left or right arrow keys, the handler invokes the store's navigation methods, enabling hands-free presenting without additional dependencies.

### Presentation Layout Components

The visual framework consists of three core components located in `src/stage/`:

- **[`Stage.tsx`](https://github.com/ConardLi/garden-skills/blob/main/Stage.tsx)** – The root wrapper that applies the current theme as a CSS custom property or className.
- **[`ChapterHost.tsx`](https://github.com/ConardLi/garden-skills/blob/main/ChapterHost.tsx)** – A dynamic renderer that mounts the React component associated with the active step index.
- **[`ProgressBar.tsx`](https://github.com/ConardLi/garden-skills/blob/main/ProgressBar.tsx)** – A visual indicator that displays advancement through the chapter array. It includes the **`data-no-step`** attribute to prevent click events from advancing slides when users interact with the progress indicator itself.

## Step-by-Step Scaffold Guide

Follow these commands to bootstrap a new presentation project from the garden-skills reference.

1. **Clone the repository and enter the starter directory.**

   ```bash
   git clone https://github.com/ConardLi/garden-skills.git
   cd garden-skills/website/web-design-website
   ```

2. **Install dependencies.**  
   The [`package.json`](https://github.com/ConardLi/garden-skills/blob/main/package.json) declares React 19, React-DOM, and Vite 8 along with TypeScript tooling.

   ```bash
   npm install
   ```

3. **Start the development server.**  
   Vite launches with Hot Module Replacement (HMR) enabled.

   ```bash
   npm run dev
   ```

   Open the localhost URL displayed in your terminal (typically `http://localhost:5173`).

4. **Define your slide chapters.**  
   Create [`src/chapters.ts`](https://github.com/ConardLi/garden-skills/blob/main/src/chapters.ts) to export an array of slide descriptors:

   ```typescript
   // src/chapters.ts
   import Intro from './slides/Intro.tsx';
   import Demo from './slides/Demo.tsx';

   export const chapters = [
     { component: Intro, theme: 'light' },
     { component: Demo, theme: 'dark' },
   ];
   ```

5. **Author slide components.**  
   Each slide receives the current step index from the global store:

   ```tsx
   // src/slides/Intro.tsx
   import { useStep } from '../store/useStep';

   export default function Intro() {
     const { step } = useStep();
     return (
       <section>
         <h1>Presentation Title</h1>
         <p>Current slide: {step + 1}</p>
       </section>
     );
   }
   ```

6. **Customize styling.**  
   Edit [`src/index.css`](https://github.com/ConardLi/garden-skills/blob/main/src/index.css) or add Tailwind CSS. The `Stage` component passes the active theme string to the DOM, allowing you to scope styles via CSS variables or class selectors.

7. **Build for production.**  
   Generate optimized static assets for offline presenting:

   ```bash
   npm run build
   ```

   This emits a `dist/` folder containing inlined JavaScript and CSS. The repository optionally provides an `npm run html` script to copy [`dist/index.html`](https://github.com/ConardLi/garden-skills/blob/main/dist/index.html) to [`article/article.html`](https://github.com/ConardLi/garden-skills/blob/main/article/article.html) for single-file distribution.

## Handling Interactive Elements and Navigation Guards

The scaffold prevents accidental slide advancement when users interact with buttons, forms, or embedded demos. The `Stage` component checks for the **`data-no-step`** attribute on click targets; if an ancestor element carries this attribute, the click handler ignores the event and does not trigger `stepStore.next()`.

Apply this guard to any interactive region:

```tsx
// Inside a slide component
<div data-no-step>
  <button onClick={() => console.log('clicked')}>Interactive Button</button>
  <input type="text" placeholder="Type here..." />
</div>

```

The `ProgressBar` component utilizes this attribute by default, ensuring that seeking through the presentation via the progress indicator does not inadvertently change the current slide.

## Summary

- **Clone the `website/web-design-website` directory** from ConardLi/garden-skills to obtain a pre-configured Vite 8 + React 19 + TypeScript environment.
- **Manage flow via `useStep`** – A minimal store in [`src/store/useStep.ts`](https://github.com/ConardLi/garden-skills/blob/main/src/store/useStep.ts) tracks chapter indices and provides `next()`/`prev()` methods without router overhead.
- **Navigate with arrows** – The `useHotKeys` hook in [`src/stage/useHotKeys.ts`](https://github.com/ConardLi/garden-skills/blob/main/src/stage/useHotKeys.ts) maps left/right arrow keys to slide transitions immediately after mounting.
- **Guard interactions** – Apply `data-no-step` to any container that should not trigger slide advancement on click, essential for live coding demos.
- **Theme per slide** – Export a `theme` property in your chapter descriptors to toggle visual modes dynamically via the `Stage` component.

## Frequently Asked Questions

### How do I prevent the presentation from advancing when I click interactive elements inside a slide?

Wrap your interactive content in a container with the **`data-no-step`** attribute. The click handler in [`App.tsx`](https://github.com/ConardLi/garden-skills/blob/main/App.tsx) checks for this attribute on the event target and its ancestors; if found, it suppresses the call to `stepStore.next()`, keeping the current slide active while allowing normal interaction with buttons, inputs, or embedded iframes.

### Can I use this scaffold for non-linear navigation or deep-linking to specific slides?

The current architecture in [`src/store/useStep.ts`](https://github.com/ConardLi/garden-skills/blob/main/src/store/useStep.ts) implements a linear integer index optimized for sequential presenting. For non-linear routing or URL-based deep-linking, you would need to replace the `useStep` store with a routing solution like React Router or TanStack Router, then map URL parameters to the chapter array indices in [`src/App.tsx`](https://github.com/ConardLi/garden-skills/blob/main/src/App.tsx).

### How do I add custom keyboard shortcuts beyond the arrow keys?

Extend the `useHotKeys` hook in [`src/stage/useHotKeys.ts`](https://github.com/ConardLi/garden-skills/blob/main/src/stage/useHotKeys.ts) by adding additional conditional checks to the keyboard event handler. For example, binding the "f" key to toggle fullscreen or "p" to open presenter notes requires importing the desired logic and registering new `if (e.key === 'f')` blocks before the cleanup function removes the listener.

### What is the typical production bundle size for offline presentations?

Because the scaffold relies on Vite's native ES-module output and tree-shaking, the final `dist/` folder typically contains only the React runtime, your slide components, and minimal state logic. According to the [`package.json`](https://github.com/ConardLi/garden-skills/blob/main/package.json) configuration using React 19 and Vite 8, expect a gzipped bundle under 100 KB for a ten-slide deck, making it ideal for USB-stick or air-gapped conference presentations.