How to Scaffold Vite + React + TypeScript Projects for Presentations
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 file registers the official @vitejs/plugin-react plugin to enable Fast Refresh during development. TypeScript safety is enforced through project references: tsconfig.app.json handles the React application context, while tsconfig.node.json isolates Vite's configuration types. The root 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, 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, 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– The root wrapper that applies the current theme as a CSS custom property or className.ChapterHost.tsx– A dynamic renderer that mounts the React component associated with the active step index.ProgressBar.tsx– A visual indicator that displays advancement through the chapter array. It includes thedata-no-stepattribute 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.
-
Clone the repository and enter the starter directory.
git clone https://github.com/ConardLi/garden-skills.git cd garden-skills/website/web-design-website -
Install dependencies.
Thepackage.jsondeclares React 19, React-DOM, and Vite 8 along with TypeScript tooling.npm install -
Start the development server.
Vite launches with Hot Module Replacement (HMR) enabled.npm run devOpen the localhost URL displayed in your terminal (typically
http://localhost:5173). -
Define your slide chapters.
Createsrc/chapters.tsto export an array of slide descriptors:// 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' }, ]; -
Author slide components.
Each slide receives the current step index from the global store:// 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> ); } -
Customize styling.
Editsrc/index.cssor add Tailwind CSS. TheStagecomponent passes the active theme string to the DOM, allowing you to scope styles via CSS variables or class selectors. -
Build for production.
Generate optimized static assets for offline presenting:npm run buildThis emits a
dist/folder containing inlined JavaScript and CSS. The repository optionally provides annpm run htmlscript to copydist/index.htmltoarticle/article.htmlfor 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:
// 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-websitedirectory from ConardLi/garden-skills to obtain a pre-configured Vite 8 + React 19 + TypeScript environment. - Manage flow via
useStep– A minimal store insrc/store/useStep.tstracks chapter indices and providesnext()/prev()methods without router overhead. - Navigate with arrows – The
useHotKeyshook insrc/stage/useHotKeys.tsmaps left/right arrow keys to slide transitions immediately after mounting. - Guard interactions – Apply
data-no-stepto any container that should not trigger slide advancement on click, essential for live coding demos. - Theme per slide – Export a
themeproperty in your chapter descriptors to toggle visual modes dynamically via theStagecomponent.
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 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 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.
How do I add custom keyboard shortcuts beyond the arrow keys?
Extend the useHotKeys hook in 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 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.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →