@funstack/static 0.0.2 → 0.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (48) hide show
  1. package/README.md +12 -0
  2. package/dist/bin/skill-installer.d.mts +1 -0
  3. package/dist/bin/skill-installer.mjs +12 -0
  4. package/dist/bin/skill-installer.mjs.map +1 -0
  5. package/dist/build/buildApp.mjs +4 -3
  6. package/dist/build/buildApp.mjs.map +1 -1
  7. package/dist/build/rscProcessor.mjs +9 -3
  8. package/dist/build/rscProcessor.mjs.map +1 -1
  9. package/dist/client/entry.d.mts +1 -0
  10. package/dist/client/entry.mjs +17 -9
  11. package/dist/client/entry.mjs.map +1 -1
  12. package/dist/client/globals.mjs.map +1 -1
  13. package/dist/docs/FAQ.md +5 -0
  14. package/dist/docs/GettingStarted.md +180 -0
  15. package/dist/docs/api/Defer.md +110 -0
  16. package/dist/docs/api/FunstackStatic.md +184 -0
  17. package/dist/docs/index.md +20 -0
  18. package/dist/docs/learn/HowItWorks.md +109 -0
  19. package/dist/docs/learn/OptimizingPayloads.md +105 -0
  20. package/dist/docs/learn/RSC.md +179 -0
  21. package/dist/docs/learn/SSR.md +104 -0
  22. package/dist/entries/client.d.mts +1 -1
  23. package/dist/entries/rsc-client.d.mts +2 -2
  24. package/dist/entries/rsc-client.mjs +2 -2
  25. package/dist/entries/server.d.mts +2 -2
  26. package/dist/plugin/index.d.mts +17 -1
  27. package/dist/plugin/index.d.mts.map +1 -1
  28. package/dist/plugin/index.mjs +10 -1
  29. package/dist/plugin/index.mjs.map +1 -1
  30. package/dist/rsc/defer.d.mts +18 -3
  31. package/dist/rsc/defer.d.mts.map +1 -1
  32. package/dist/rsc/defer.mjs +34 -14
  33. package/dist/rsc/defer.mjs.map +1 -1
  34. package/dist/rsc/entry.d.mts.map +1 -1
  35. package/dist/rsc/entry.mjs +85 -20
  36. package/dist/rsc/entry.mjs.map +1 -1
  37. package/dist/rsc-client/clientWrapper.d.mts +3 -3
  38. package/dist/rsc-client/clientWrapper.d.mts.map +1 -1
  39. package/dist/rsc-client/clientWrapper.mjs +2 -2
  40. package/dist/rsc-client/clientWrapper.mjs.map +1 -1
  41. package/dist/rsc-client/entry.d.mts +1 -1
  42. package/dist/rsc-client/entry.mjs +1 -1
  43. package/dist/ssr/entry.d.mts +2 -0
  44. package/dist/ssr/entry.d.mts.map +1 -1
  45. package/dist/ssr/entry.mjs +6 -2
  46. package/dist/ssr/entry.mjs.map +1 -1
  47. package/package.json +26 -11
  48. package/skills/funstack-static-knowledge/SKILL.md +44 -0
@@ -0,0 +1,184 @@
1
+ # funstackStatic()
2
+
3
+ The `funstackStatic()` function is the main Vite plugin that enables React Server Components for building SPAs without a runtime server.
4
+
5
+ ## Import
6
+
7
+ ```typescript
8
+ import funstackStatic from "@funstack/static";
9
+ ```
10
+
11
+ ## Usage
12
+
13
+ ```typescript
14
+ // vite.config.ts
15
+ import funstackStatic from "@funstack/static";
16
+ import { defineConfig } from "vite";
17
+
18
+ export default defineConfig({
19
+ plugins: [
20
+ funstackStatic({
21
+ root: "./src/root.tsx",
22
+ app: "./src/App.tsx",
23
+ }),
24
+ ],
25
+ });
26
+ ```
27
+
28
+ ## Options
29
+
30
+ ### root (required)
31
+
32
+ **Type:** `string`
33
+
34
+ Path to the root component file. This component wraps your entire application and defines the HTML document structure (`<html>`, `<head>`, `<body>`).
35
+
36
+ ```typescript
37
+ funstackStatic({
38
+ root: "./src/root.tsx",
39
+ // ...
40
+ });
41
+ ```
42
+
43
+ The root component receives `children` as a prop:
44
+
45
+ ```tsx
46
+ // src/root.tsx
47
+ export default function Root({ children }: { children: React.ReactNode }) {
48
+ return (
49
+ <html lang="en">
50
+ <head>
51
+ <meta charSet="UTF-8" />
52
+ <title>My Site</title>
53
+ </head>
54
+ <body>{children}</body>
55
+ </html>
56
+ );
57
+ }
58
+ ```
59
+
60
+ ### app (required)
61
+
62
+ **Type:** `string`
63
+
64
+ Path to the app component file. This component defines your application's content.
65
+
66
+ ```typescript
67
+ funstackStatic({
68
+ root: "./src/root.tsx",
69
+ app: "./src/App.tsx",
70
+ });
71
+ ```
72
+
73
+ ```tsx
74
+ // src/App.tsx
75
+
76
+ export default function App() {
77
+ return (
78
+ <div>
79
+ <h1>Welcome to My Site</h1>
80
+ {/* Your fantastic application goes here */}
81
+ </div>
82
+ );
83
+ }
84
+ ```
85
+
86
+ **Note:** if your app has multiple pages, you can use a routing library here just like in a traditional SPA.
87
+
88
+ ### publicOutDir (optional)
89
+
90
+ **Type:** `string`
91
+ **Default:** `"dist/public"`
92
+
93
+ Output directory for the generated static files.
94
+
95
+ ```typescript
96
+ funstackStatic({
97
+ root: "./src/root.tsx",
98
+ app: "./src/App.tsx",
99
+ publicOutDir: "build/static",
100
+ });
101
+ ```
102
+
103
+ ### ssr (optional)
104
+
105
+ **Type:** `boolean`
106
+ **Default:** `false`
107
+
108
+ Enable server-side rendering of the App component.
109
+
110
+ When `false` (default), only the Root shell is rendered to HTML at build time. The App component's RSC payload is fetched separately and rendered client-side using `createRoot`. This results in faster initial HTML delivery but requires JavaScript to display the App content.
111
+
112
+ When `true`, both the Root and App components are fully rendered to HTML. The client hydrates the existing HTML using `hydrateRoot`, which can improve perceived performance and SEO since the full content is visible before JavaScript loads.
113
+
114
+ ```typescript
115
+ funstackStatic({
116
+ root: "./src/root.tsx",
117
+ app: "./src/App.tsx",
118
+ ssr: true, // Enable full SSR
119
+ });
120
+ ```
121
+
122
+ **Note:** In both modes, React Server Components are used - the `ssr` option only controls whether the App's HTML is pre-rendered or rendered client-side.
123
+
124
+ ### clientInit (optional)
125
+
126
+ **Type:** `string`
127
+
128
+ Path to a module that runs on the client side **before React hydration**. Use this for client-side instrumentation like Sentry, analytics, or feature flags.
129
+
130
+ The module is imported for its side effects only - no exports are needed.
131
+
132
+ ```typescript
133
+ funstackStatic({
134
+ root: "./src/root.tsx",
135
+ app: "./src/App.tsx",
136
+ clientInit: "./src/client-init.ts",
137
+ });
138
+ ```
139
+
140
+ Example client init file:
141
+
142
+ ```typescript
143
+ // src/client-init.ts
144
+ import * as Sentry from "@sentry/browser";
145
+
146
+ Sentry.init({
147
+ dsn: "https://your-sentry-dsn",
148
+ environment: import.meta.env.MODE,
149
+ });
150
+ ```
151
+
152
+ **Note:** Errors in the client init module will propagate normally and prevent the app from rendering.
153
+
154
+ ## Full Example
155
+
156
+ ```typescript
157
+ // vite.config.ts
158
+ import { funstackStatic } from "@funstack/static";
159
+ import { defineConfig } from "vite";
160
+
161
+ export default defineConfig({
162
+ plugins: [
163
+ funstackStatic({
164
+ root: "./src/root.tsx",
165
+ app: "./src/App.tsx",
166
+ publicOutDir: "dist/public",
167
+ }),
168
+ ],
169
+ });
170
+ ```
171
+
172
+ ## Vite Commands
173
+
174
+ You can use the same Vite commands you would use in a normal Vite project:
175
+
176
+ - `vite dev` - Starts the development server
177
+ - `vite build` - Builds the static files
178
+ - `vite preview` - Previews the built static files locally
179
+
180
+ ## See Also
181
+
182
+ - [Getting Started](/funstack-static/getting-started) - Quick start guide
183
+ - [defer()](/funstack-static/api/defer) - Deferred rendering for streaming
184
+ - [React Server Components](/funstack-static/learn/rsc) - Understanding RSC
@@ -0,0 +1,20 @@
1
+ # @funstack/static Documentation
2
+
3
+ A Vite plugin for building static sites with React Server Components.
4
+
5
+ ## Available Documentation
6
+
7
+ - [FAQ](./FAQ.md) - This is a bug in React itself. Please wait for [the fix](https://github.com/facebook/react/pull/34760) to be released.
8
+ - [Getting Started](./GettingStarted.md) - Welcome to **FUNSTACK Static**! Build high-performance Single Page Applications powered by React Server Components - no server required at runtime.
9
+
10
+ ### API
11
+
12
+ - [defer()](./api/Defer.md) - The `defer()` function enables deferred rendering for React Server Components, reducing initial data load.
13
+ - [funstackStatic()](./api/FunstackStatic.md) - The `funstackStatic()` function is the main Vite plugin that enables React Server Components for building SPAs without a runtime server.
14
+
15
+ ### Learn
16
+
17
+ - [How It Works](./learn/HowItWorks.md) - FUNSTACK Static is a React framework that leverages React Server Components (RSC) to build a fully static Single Page Application. The result is a set of files that can be deployed to **any static file hosting service** - no server required at runtime.
18
+ - [Optimizing RSC Payloads](./learn/OptimizingPayloads.md) - FUNSTACK Static uses React Server Components (RSC) to pre-render your application at build time. By default, all content is bundled into a single RSC payload. This page explains how to split that payload into smaller chunks for better loading performance.
19
+ - [React Server Components](./learn/RSC.md) - [React Server Components (RSC)](https://react.dev/reference/rsc/server-components) are a new paradigm for building React applications where components can run on the server (or at build time) rather than in the browser.
20
+ - [Server-Side Rendering](./learn/SSR.md) - In FUNSTACK Static, **Server-Side Rendering (SSR)** means a build-time process that pre-renders your React components (including client components) to HTML. This can make the initial paint faster.
@@ -0,0 +1,109 @@
1
+ # How It Works
2
+
3
+ FUNSTACK Static is a React framework that leverages React Server Components (RSC) to build a fully static Single Page Application. The result is a set of files that can be deployed to **any static file hosting service** - no server required at runtime.
4
+
5
+ FUNSTACK Static is built on top of [@vitejs/plugin-rsc](https://www.npmjs.com/package/@vitejs/plugin-rsc) for its RSC support.
6
+
7
+ ## Architecture
8
+
9
+ FUNSTACK Static applications have **a single entrypoint** which is a server component. This component is responsible for rendering the entire application.
10
+
11
+ ```tsx
12
+ // src/App.tsx
13
+ export default function App() {
14
+ return (
15
+ <div>
16
+ <h1>Welcome to my FUNSTACK Static app!</h1>
17
+ {/* Your app components go here */}
18
+ </div>
19
+ );
20
+ }
21
+ ```
22
+
23
+ Currently, no routing solution is built in. If you need routing, you can use your favorite routing library for SPAs, such as [React Router](https://reactrouter.com/) (SPA mode) or [FUNSTACK Router](https://github.com/uhyo/funstack-router).
24
+
25
+ Server components cannot have any client-side interactivity. To add interactivity, you can use client components. FUNSTACK Static follows [the standard React Server Components conventions](https://react.dev/reference/rsc/server-components#adding-interactivity-to-server-components) (`"use client"`) for defining client components.
26
+
27
+ Behind the scenes, server components are rendered into **RSC payloads** at build time (or inside the development server in development mode). These payloads are then loaded by the client-side React application to reflect the server-rendered content to the DOM.
28
+
29
+ On production mode builds, FUNSTACK Static generates a set of static files that include:
30
+
31
+ - An `index.html` file that bootstraps the client-side React application
32
+ - JavaScript files for the client-side React application and its dependencies
33
+ - Asset files (CSS, images, etc.)
34
+ - RSC payload files generated from server components
35
+
36
+ Typically, the output structure looks like this:
37
+
38
+ ```
39
+ dist/public
40
+ ├── assets
41
+ │ ├── app-91R2BjDJ.css
42
+ │ ├── app-CQU2Svmn.js
43
+ │ ├── app-ovZqc1Hu.css
44
+ │ ├── index-CCEDZan_.js
45
+ │ ├── root-DvE5ENz2.css
46
+ │ └── rsc-D0fjt5Ie.js
47
+ ├── funstack__
48
+ │ └── fun:rsc-payload
49
+ │ └── db1923b9b6507ab4.txt
50
+ └── index.html
51
+ ```
52
+
53
+ The RSC payload files under `funstack__` are loaded by the client-side code to bootstrap the application with server-rendered content.
54
+
55
+ This can been seen as an **optimized version of traditional client-only SPAs**, where the entire application is bundled into JavaScript files. By using RSC, some of the rendering work is offloaded to the build time, resulting in smaller JavaScript bundles combined with RSC payloads that require less client-side processing (parsing is easier, no JavaScript execution needed).
56
+
57
+ ## Root Entry Point
58
+
59
+ The root entry point of a FUNSTACK Static application is defined in the `funstack.config.js` file using the `root` option. This is a **special endpoint** that defines the HTML shell of your application. This is a separate endpoint from the main App component.
60
+
61
+ A typical Root component looks like this:
62
+
63
+ ```tsx
64
+ // src/Root.tsx
65
+ export default function Root({ children }: { children: React.ReactNode }) {
66
+ return (
67
+ <html lang="en">
68
+ <head>
69
+ <meta charSet="UTF-8" />
70
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
71
+ <title>My FUNSTACK Static App</title>
72
+ </head>
73
+ <body>{children}</body>
74
+ </html>
75
+ );
76
+ }
77
+ ```
78
+
79
+ The `children` prop contains the main App component.
80
+
81
+ The Root component is special in two ways:
82
+
83
+ 1. Rendered into a fully static HTML file (`index.html`), not an RSC payload.
84
+ 2. Is **not hydrated** on the client side. This means that any client-side interactivity (e.g., event handlers) inside the Root component will not work.
85
+
86
+ The Root entrypoint is a FUNSTACK Static counterpart to the `index.html` file in traditional SPAs. It allows you to still leverage some of the benefits of server components for defining the HTML shell of your application.
87
+
88
+ ## Server-Side Rendering
89
+
90
+ By default, FUNSTACK Static only renders the Root shell to HTML. The App component is rendered client-side from its RSC payload. This behavior keeps the initial HTML small and fast to deliver.
91
+
92
+ If you want the App component to also be rendered to HTML (for better SEO or perceived performance), you can enable the `ssr` option:
93
+
94
+ ```typescript
95
+ funstackStatic({
96
+ root: "./src/root.tsx",
97
+ app: "./src/App.tsx",
98
+ ssr: true,
99
+ });
100
+ ```
101
+
102
+ With `ssr: true`, the full page content is visible in the HTML before JavaScript loads. The client then hydrates the existing HTML instead of rendering from scratch.
103
+
104
+ Note that in both modes, React Server Components are still used - the `ssr` option only controls whether the App's HTML is pre-rendered at build time or rendered client-side.
105
+
106
+ ## See Also
107
+
108
+ - [React Server Components](/funstack-static/learn/rsc) - Understanding RSC in depth
109
+ - [Getting Started](/funstack-static/getting-started) - Set up your first project
@@ -0,0 +1,105 @@
1
+ # Optimizing RSC Payloads
2
+
3
+ FUNSTACK Static uses React Server Components (RSC) to pre-render your application at build time. By default, all content is bundled into a single RSC payload. This page explains how to split that payload into smaller chunks for better loading performance.
4
+
5
+ ## The Default Behavior
6
+
7
+ Without any optimization, your entire application is rendered into one RSC payload file:
8
+
9
+ ```
10
+ dist/public/funstack__/
11
+ └── fun:rsc-payload/
12
+ └── b62ec6668fd49300.txt ← Contains everything
13
+ ```
14
+
15
+ This means users must download the entire payload before seeing any content - even if they only need one page of a multi-page application.
16
+
17
+ **Note:** RSC payloads contain the rendering results of server components only; client components and their JavaScript bundles are handled separately. However, it is still important to optimize RSC payload sizes because the recommended best practice is to keep as much of your UI as server components as possible.
18
+
19
+ ## Chunking with defer()
20
+
21
+ The `defer()` function lets you split your application into multiple RSC payloads. Each `defer()` call creates a separate payload file that loads on-demand when that component renders.
22
+
23
+ ```tsx
24
+ import { defer } from "@funstack/static/server";
25
+
26
+ // Instead of this:
27
+ <HeavyContent />
28
+
29
+ // Do this:
30
+ <Suspense fallback={<p>Loading...</p>}>
31
+ {defer(<HeavyContent />)}
32
+ </Suspense>
33
+ ```
34
+
35
+ The content inside `defer()` is still rendered at build time, but it's stored in a separate file and fetched only when needed.
36
+
37
+ **Note:** use of `defer()` requires a `<Suspense>` boundary to handle the loading state while the payload is being fetched.
38
+
39
+ ## Route-Level Optimization
40
+
41
+ The most impactful use of `defer()` is wrapping your route components. This ensures users only download the payload for the page they're viewing:
42
+
43
+ ```tsx
44
+ import { defer } from "@funstack/static/server";
45
+ import { route } from "@funstack/router/server";
46
+ import HomePage from "./pages/Home";
47
+ import AboutPage from "./pages/About";
48
+ import DocsPage from "./pages/Docs";
49
+
50
+ const routes = [
51
+ route({
52
+ path: "/",
53
+ component: defer(<HomePage />),
54
+ }),
55
+ route({
56
+ path: "/about",
57
+ component: defer(<AboutPage />),
58
+ }),
59
+ route({
60
+ path: "/docs",
61
+ component: defer(<DocsPage />),
62
+ }),
63
+ ];
64
+ ```
65
+
66
+ With this setup:
67
+
68
+ - Visiting `/` only downloads the Home page payload
69
+ - Navigating to `/about` fetches the About page payload on-demand
70
+ - Users see content faster because they're not waiting for pages they haven't visited
71
+
72
+ ## Output Structure
73
+
74
+ After building with route-level `defer()`, your output looks like this:
75
+
76
+ ```
77
+ dist/public/funstack__/
78
+ └── fun:rsc-payload/
79
+ ├── a3f2b1c9d8e7f6a5.txt ← Home page
80
+ ├── b5698be72eea3c37.txt ← About page
81
+ ├── b62ec6668fd49300.txt ← Main app shell
82
+ └── c7d8e9f0a1b2c3d4.txt ← Docs page
83
+ ```
84
+
85
+ Each payload file has a **content-based hash** in its filename. This enables aggressive browser caching - the file only changes when its content changes.
86
+
87
+ **Note:** during development, UUIDs are used instead of content hashes for faster rebuilds. Content hashes are applied during production builds.
88
+
89
+ ## Best Practices
90
+
91
+ **Always wrap route components** - This is the single most important optimization. It prevents users from downloading content for pages they may never visit.
92
+
93
+ **Use Suspense boundaries** - Every `defer()` call must be inside a `<Suspense>` boundary. The fallback is shown while the payload is being fetched.
94
+
95
+ ```tsx
96
+ <Suspense fallback={<PageSkeleton />}>{defer(<PageContent />)}</Suspense>
97
+ ```
98
+
99
+ **Consider below-the-fold content** - Content hidden in collapsed sections, tabs, or modals is a good candidate for `defer()` since users may never need it.
100
+
101
+ ## See Also
102
+
103
+ - [defer()](/funstack-static/api/defer) - API reference with full signature and technical details
104
+ - [How It Works](/funstack-static/learn/how-it-works) - Overall FUNSTACK Static architecture
105
+ - [React Server Components](/funstack-static/learn/rsc) - Understanding RSC fundamentals
@@ -0,0 +1,179 @@
1
+ # React Server Components
2
+
3
+ [React Server Components (RSC)](https://react.dev/reference/rsc/server-components) are a new paradigm for building React applications where components can run on the server (or at build time) rather than in the browser.
4
+
5
+ ## What Are Server Components?
6
+
7
+ Server Components are React components that:
8
+
9
+ - **Run on the server/build time** - Not in the browser
10
+ - **Can be async** - Use `async/await` directly in components
11
+ - **Have zero client bundle impact** - Their code never ships to the browser
12
+ - **Can access server-side resources** - Files, databases, environment variables
13
+
14
+ ```tsx
15
+ // This component runs at build time, not in the browser
16
+ async function UserList() {
17
+ // Direct file system access
18
+ const data = await fs.readFile("./data/users.json", "utf-8");
19
+ const users = JSON.parse(data);
20
+
21
+ return (
22
+ <ul>
23
+ {users.map((user) => (
24
+ <li key={user.id}>{user.name}</li>
25
+ ))}
26
+ </ul>
27
+ );
28
+ }
29
+ ```
30
+
31
+ ## FUNSTACK Static and RSC
32
+
33
+ FUNSTACK Static is a React framework **without a runtime server** that still leverages React Server Components to **improve performance of traditional SPAs**.
34
+
35
+ All server components are rendered at **build time**, producing RSC Payloads that are fetched by the client (browser) at runtime.
36
+
37
+ While dynamic server-side logic isn't possible with FUNSTACK Static, you can still benefit from RSC features like:
38
+
39
+ - Reduced bundle size
40
+ - Build-time data fetching
41
+ - Async components
42
+
43
+ ```tsx
44
+ // Build-time data fetching with an async Server Component
45
+ async function BlogPost({ slug }: { slug: string }) {
46
+ // Runs during build, not at runtime
47
+ const content = await fetchMarkdownFile(`./posts/${slug}.md`);
48
+
49
+ return (
50
+ <article>
51
+ <Markdown content={content} />
52
+ </article>
53
+ );
54
+ }
55
+ ```
56
+
57
+ **Note:** in FUNSTACK Static, async data is fetched at build time. For truly dynamic data, you'll need client-side JavaScript and fetch data as you would do in pre-RSC SPAs.
58
+
59
+ ## Composing Server and Client Components
60
+
61
+ While Server Components can't use hooks or browser APIs, they can render Client Components that do:
62
+
63
+ ```tsx
64
+ // Server Component (runs at build time)
65
+ async function ProductPage({ id }: { id: string }) {
66
+ const product = await fetchProduct(id);
67
+
68
+ return (
69
+ <div>
70
+ <h1>{product.name}</h1>
71
+ <p>{product.description}</p>
72
+
73
+ {/* Client Component for interactivity */}
74
+ <AddToCartButton productId={id} />
75
+ </div>
76
+ );
77
+ }
78
+ ```
79
+
80
+ ```tsx
81
+ // Client Component (runs in browser)
82
+ "use client";
83
+
84
+ import { useState } from "react";
85
+
86
+ export function AddToCartButton({ productId }: { productId: string }) {
87
+ const [added, setAdded] = useState(false);
88
+
89
+ return (
90
+ <button onClick={() => setAdded(true)}>
91
+ {added ? "Added!" : "Add to Cart"}
92
+ </button>
93
+ );
94
+ }
95
+ ```
96
+
97
+ ## Benefits for SPAs
98
+
99
+ ### 1. Reduced Bundle Size
100
+
101
+ Unlike traditional SPAs where all code ships to the client, Server Components never reach the browser. Only static HTML (and glue code for client components) is sent.
102
+
103
+ ### 2. Reduced Warm-Up Time
104
+
105
+ The browser does't need to execute Server Component code, leading to less JavaScript to parse and run on initial load. Only ship the code needed for interactivity.
106
+
107
+ ### 3. Build-Time Data Fetching
108
+
109
+ Fetch data once at build time, embedded directly into your pre-rendered HTML:
110
+
111
+ ```tsx
112
+ async function BlogIndex() {
113
+ // Fetched once during build, not on every page view
114
+ const posts = await fetchAllPosts();
115
+ return <PostList posts={posts} />;
116
+ }
117
+ ```
118
+
119
+ ### 4. Full SPA Interactivity
120
+
121
+ On the browser, your app behaves like any SPA - client-side navigation, state management, and all the interactivity you need:
122
+
123
+ ```tsx
124
+ // Client Component for interactive features
125
+ "use client";
126
+
127
+ import { useState } from "react";
128
+ import { useNavigate } from "@funstack/router";
129
+
130
+ export function SearchBox() {
131
+ const [query, setQuery] = useState("");
132
+ const navigate = useNavigate();
133
+
134
+ return (
135
+ <input
136
+ value={query}
137
+ onChange={(e) => setQuery(e.target.value)}
138
+ onKeyDown={(e) => {
139
+ if (e.key === "Enter") {
140
+ navigate(`/search?q=${query}`);
141
+ }
142
+ }}
143
+ />
144
+ );
145
+ }
146
+ ```
147
+
148
+ ### 5. Type-Safe Data Flow
149
+
150
+ Data flows from Server Components to Client Components with full TypeScript support:
151
+
152
+ ```tsx
153
+ interface Post {
154
+ id: string;
155
+ title: string;
156
+ content: string;
157
+ }
158
+
159
+ async function BlogPost({ slug }: { slug: string }): Promise<JSX.Element> {
160
+ const post: Post = await fetchPost(slug);
161
+ return <Article post={post} />;
162
+ }
163
+ ```
164
+
165
+ ## Considerations
166
+
167
+ When using FUNSTACK Static, keep in mind:
168
+
169
+ 1. **Build-time data** - Server Component data is fetched during build. For runtime data, use Client Components with standard fetch patterns.
170
+ 2. **No server context** - No access to cookies, headers, or request data in Server Components.
171
+ 3. **Only one entrypoint** - FUNSTACK Static apps are single-page applications, without any routing built in. All routing and navigation must be handled client-side.
172
+
173
+ This makes FUNSTACK Static ideal for developers looking to leverage RSC benefits while deploying simple static SPAs without server infrastructure.
174
+
175
+ ## See Also
176
+
177
+ - [Getting Started](/funstack-static/getting-started) - Set up your first project
178
+ - [defer()](/funstack-static/api/defer) - Stream content progressively
179
+ - [funstackStatic()](/funstack-static/api/funstack-static) - Plugin configuration