From c15b4df70e0823fcc37ce904bdfc02ee7cdaba56 Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 2 Feb 2026 00:31:50 +0000 Subject: [PATCH 1/2] docs: add Migrating from Vite SPA guide Add a new documentation page under Getting Started that helps users migrate their existing Vite React SPA to FUNSTACK Static. https://claude.ai/code/session_01EizKY1etJCeKMhsPKqJkfM --- packages/docs/src/App.tsx | 9 + .../docs/src/components/Sidebar/Sidebar.tsx | 4 + .../docs/src/pages/MigratingFromViteSPA.mdx | 302 ++++++++++++++++++ 3 files changed, 315 insertions(+) create mode 100644 packages/docs/src/pages/MigratingFromViteSPA.mdx diff --git a/packages/docs/src/App.tsx b/packages/docs/src/App.tsx index 1fabe24..ef3fbdd 100644 --- a/packages/docs/src/App.tsx +++ b/packages/docs/src/App.tsx @@ -11,6 +11,7 @@ import RSCConcept from "./pages/learn/RSC.mdx"; import SSR from "./pages/learn/SSR.mdx"; import FAQ from "./pages/FAQ.mdx"; import GettingStarted from "./pages/GettingStarted.mdx"; +import MigratingFromViteSPA from "./pages/MigratingFromViteSPA.mdx"; import { Home } from "./pages/Home"; import { NotFound } from "./pages/NotFound"; import { Router } from "./Router"; @@ -36,6 +37,14 @@ const routes: RouteDefinition[] = [ ), }), + route({ + path: "/getting-started/migrating-from-vite-spa", + component: ( + + {defer(, { name: "MigratingFromViteSPA" })} + + ), + }), route({ path: "/faq", component: {defer(, { name: "FAQ" })}, diff --git a/packages/docs/src/components/Sidebar/Sidebar.tsx b/packages/docs/src/components/Sidebar/Sidebar.tsx index 36fcf85..a69ab39 100644 --- a/packages/docs/src/components/Sidebar/Sidebar.tsx +++ b/packages/docs/src/components/Sidebar/Sidebar.tsx @@ -15,6 +15,10 @@ export const navigation: NavSection[] = [ title: "Getting Started", items: [ { label: "Introduction", href: "/funstack-static/getting-started" }, + { + label: "Migrating from Vite SPA", + href: "/funstack-static/getting-started/migrating-from-vite-spa", + }, ], }, { diff --git a/packages/docs/src/pages/MigratingFromViteSPA.mdx b/packages/docs/src/pages/MigratingFromViteSPA.mdx new file mode 100644 index 0000000..72f7da0 --- /dev/null +++ b/packages/docs/src/pages/MigratingFromViteSPA.mdx @@ -0,0 +1,302 @@ +# Migrating from Vite SPA + +Already have a Vite-powered React SPA? This guide walks you through migrating to FUNSTACK Static to unlock React Server Components and improved performance. + +## Overview + +Migrating from a standard Vite React SPA to FUNSTACK Static involves: + +1. Installing FUNSTACK Static +2. Adding the Vite plugin +3. Restructuring your entry point into Root and App components +4. Converting appropriate components to Server Components + +The good news: your existing client components work as-is. You can migrate incrementally. + +## Step 1: Install Dependencies + +Add FUNSTACK Static to your existing project: + +```bash +npm install @funstack/static +``` + +Or with pnpm: + +```bash +pnpm add @funstack/static +``` + +## Step 2: Update Vite Config + +Modify your `vite.config.ts` to add the FUNSTACK Static plugin: + +```typescript +import { funstackStatic } from "@funstack/static"; +import react from "@vitejs/plugin-react"; +import { defineConfig } from "vite"; + +export default defineConfig({ + plugins: [ + funstackStatic({ + root: "./src/Root.tsx", + app: "./src/App.tsx", + }), + react(), + ], +}); +``` + +## Step 3: Create the Root Component + +The Root component replaces your `index.html` file. Create `src/Root.tsx`: + +```tsx +// src/Root.tsx +import type React from "react"; + +export default function Root({ children }: { children: React.ReactNode }) { + return ( + + + + + My App + {/* Add your existing content here */} + + +
{children}
+ + + ); +} +``` + +Move any content from your `index.html` `` section into this component. + +## Step 4: Update Your App Entry Point + +Your existing `main.tsx` or `index.tsx` likely looks like this: + +```tsx +// Before: src/main.tsx +import React from "react"; +import ReactDOM from "react-dom/client"; +import App from "./App"; +import "./index.css"; + +ReactDOM.createRoot(document.getElementById("root")!).render( + + + , +); +``` + +With FUNSTACK Static, you no longer need this file. Instead, your `App.tsx` becomes the entry point and is automatically rendered as a Server Component: + +```tsx +// After: src/App.tsx +import "./index.css"; +import { HomePage } from "./pages/HomePage"; + +export default function App() { + return ; +} +``` + +You can delete `main.tsx` - FUNSTACK Static handles the rendering. + +## Step 5: Mark Client Components + +In a standard Vite SPA, all components are client components. With FUNSTACK Static, components are Server Components by default. + +Add `"use client"` to components that need interactivity: + +```tsx +// src/components/Counter.tsx +"use client"; + +import { useState } from "react"; + +export function Counter() { + const [count, setCount] = useState(0); + return ; +} +``` + +Components need `"use client"` if they: + +- Use React hooks (`useState`, `useEffect`, `useContext`, etc.) +- Attach event handlers (`onClick`, `onChange`, etc.) +- Use browser-only APIs (`window`, `document`, `localStorage`, etc.) + +## Step 6: Update Your Router (If Applicable) + +If you're using React Router or another client-side router, you have two options: + +### Option A: Keep Your Existing Router + +You can continue using your existing router as a client component: + +```tsx +// src/App.tsx +import { ClientRouter } from "./ClientRouter"; + +export default function App() { + return ; +} +``` + +```tsx +// src/ClientRouter.tsx +"use client"; + +import { BrowserRouter, Routes, Route } from "react-router-dom"; +import { Home } from "./pages/Home"; +import { About } from "./pages/About"; + +export function ClientRouter() { + return ( + + + } /> + } /> + + + ); +} +``` + +### Option B: Use a Server-Compatible Router + +For better performance, consider using `@funstack/router` which supports Server Components: + +```bash +npm install @funstack/router +``` + +```tsx +// src/App.tsx +import { Router } from "@funstack/router"; +import { route } from "@funstack/router/server"; +import { Home } from "./pages/Home"; +import { About } from "./pages/About"; + +const routes = [ + route({ path: "/", component: }), + route({ path: "/about", component: }), +]; + +export default function App() { + return ; +} +``` + +## Step 7: Delete Unnecessary Files + +After migration, you can remove: + +- `src/main.tsx` (or `src/index.tsx`) - no longer needed +- `index.html` - replaced by `Root.tsx` + +## Common Migration Patterns + +### Global Styles + +Import global CSS in your `App.tsx`: + +```tsx +// src/App.tsx +import "./index.css"; +import "./global.css"; + +export default function App() { + // ... +} +``` + +### Context Providers + +Wrap client-side providers in a client component: + +```tsx +// src/Providers.tsx +"use client"; + +import { ThemeProvider } from "./ThemeContext"; +import { AuthProvider } from "./AuthContext"; + +export function Providers({ children }: { children: React.ReactNode }) { + return ( + + {children} + + ); +} +``` + +```tsx +// src/App.tsx +import { Providers } from "./Providers"; +import { HomePage } from "./pages/HomePage"; + +export default function App() { + return ( + + + + ); +} +``` + +### Environment Variables + +Client-side environment variables still work the same way with the `VITE_` prefix: + +```tsx +// In client components +const apiUrl = import.meta.env.VITE_API_URL; +``` + +## Verifying the Migration + +Run the development server: + +```bash +npm run dev +``` + +Check that: + +- Your app loads correctly +- Interactive components work (clicks, form inputs, etc.) +- Routing works as expected +- Styles are applied correctly + +Build for production: + +```bash +npm run build +``` + +Your static files will be generated in `dist/public`, ready for deployment. + +## Troubleshooting + +### "Cannot use hooks in Server Component" + +Add `"use client"` at the top of components that use React hooks. + +### "window is not defined" + +Components using browser APIs must be client components. Add `"use client"` directive. + +### Styles not loading + +Ensure CSS imports are in `App.tsx` or in client components that are actually rendered. + +## What's Next? + +- Learn about [defer()](/funstack-static/api/defer) for code splitting Server Components +- Explore [Optimizing RSC Payloads](/funstack-static/learn/optimizing-payloads) for better performance +- Understand [How It Works](/funstack-static/learn/how-it-works) under the hood From c97559247151795f3166a8afe89e9c48d24c0d97 Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 2 Feb 2026 03:14:27 +0000 Subject: [PATCH 2/2] docs: clarify "use client" is only needed at server/client boundaries Update Step 5 to explain that the directive is only required for components directly imported by Server Components, not every component that uses hooks or event handlers. https://claude.ai/code/session_01EizKY1etJCeKMhsPKqJkfM --- packages/docs/src/pages/MigratingFromViteSPA.mdx | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/packages/docs/src/pages/MigratingFromViteSPA.mdx b/packages/docs/src/pages/MigratingFromViteSPA.mdx index 72f7da0..f157403 100644 --- a/packages/docs/src/pages/MigratingFromViteSPA.mdx +++ b/packages/docs/src/pages/MigratingFromViteSPA.mdx @@ -106,25 +106,28 @@ export default function App() { You can delete `main.tsx` - FUNSTACK Static handles the rendering. -## Step 5: Mark Client Components +## Step 5: Mark Client Component Boundaries In a standard Vite SPA, all components are client components. With FUNSTACK Static, components are Server Components by default. -Add `"use client"` to components that need interactivity: +You only need to add `"use client"` to components that are **directly imported by Server Components**. This marks the boundary between server and client code. Components imported by other client components don't need the directive. ```tsx // src/components/Counter.tsx "use client"; import { useState } from "react"; +import { Button } from "./Button"; // No "use client" needed in Button.tsx export function Counter() { const [count, setCount] = useState(0); - return ; + return ; } ``` -Components need `"use client"` if they: +In this example, `Counter.tsx` needs `"use client"` because it's imported by a Server Component. But `Button.tsx` doesn't need it since it's only imported by `Counter`, which is already a client component. + +A component is a client component if it: - Use React hooks (`useState`, `useEffect`, `useContext`, etc.) - Attach event handlers (`onClick`, `onChange`, etc.)