Basic Server Component
Section titled “Basic Server Component”export default function App() { return <h1>Hello from a Server Component!</h1>;}Server + Client Components
Section titled “Server + Client Components”import Counter from './Counter';
export default function App() { return ( <div> <h1>Server Component</h1> <p>This text is rendered on the server.</p> <Counter /> </div> );}'use client';import { useState } from 'react';
export default function Counter() { const [count, setCount] = useState(0); return ( <button onClick={() => setCount(count + 1)}> Count: {count} </button> );}Async Server Component with Suspense
Section titled “Async Server Component with Suspense”import { Suspense } from 'react';import Albums from './Albums';
export default function App() { return ( <div> <h1>Music</h1> <Suspense fallback={<p>Loading albums...</p>}> <Albums /> </Suspense> </div> );}async function fetchAlbums() { await new Promise(resolve => setTimeout(resolve, 1000)); return ['Abbey Road', 'Let It Be', 'Revolver'];}
export default async function Albums() { const albums = await fetchAlbums(); return ( <ul> {albums.map(album => ( <li key={album}>{album}</li> ))} </ul> );}Streaming Proof
Section titled “Streaming Proof”This demo proves streaming is incremental. The shell renders instantly with a <Suspense> fallback. After 2 seconds the async component streams in and replaces it — without re-rendering the outer content. The timestamps show the gap.
import { Suspense } from 'react';import SlowData from './SlowData';import Timestamp from './Timestamp';
export default function App() { return ( <div> <h1>Streaming Proof</h1> <p>Shell rendered at: <Timestamp /></p> <Suspense fallback={<p>⏳ Waiting for data to stream in...</p>}> <SlowData /> </Suspense> </div> );}import Timestamp from './Timestamp';
async function fetchData() { await new Promise(resolve => setTimeout(resolve, 2000)); return ['Chunk A', 'Chunk B', 'Chunk C'];}
export default async function SlowData() { const items = await fetchData(); return ( <div> <p>Data streamed in at: <Timestamp /></p> <ul> {items.map(item => ( <li key={item}>{item}</li> ))} </ul> </div> );}'use client';
export default function Timestamp() { return <strong>{new Date().toLocaleTimeString()}</strong>;}Flight Data Types
Section titled “Flight Data Types”This demo passes Map, Set, Date, and BigInt from a server component through the Flight stream to a client component, proving the full Flight protocol type system works end-to-end.
import DataViewer from './DataViewer';
export default function App() { const map = new Map([ ['alice', 100], ['bob', 200], ]); const set = new Set(['react', 'next', 'remix']); const date = new Date('2025-06-15T12:00:00Z'); const big = 9007199254740993n;
return ( <div> <h1>Flight Data Types</h1> <DataViewer map={map} set={set} date={date} big={big} /> </div> );}'use client';
export default function DataViewer({ map, set, date, big }) { const checks = [ ['Map', map instanceof Map, () => ( <ul>{[...map.entries()].map(([k, v]) => <li key={k}>{k}: {v}</li>)}</ul> )], ['Set', set instanceof Set, () => ( <ul>{[...set].map(v => <li key={v}>{v}</li>)}</ul> )], ['Date', date instanceof Date, () => ( <p>{date.toISOString()}</p> )], ['BigInt', typeof big === 'bigint', () => ( <p>{big.toString()}</p> )], ];
return ( <div> {checks.map(([label, passed, render]) => ( <div key={label} style={{ marginBottom: 12 }}> <strong>{label}: {passed ? 'pass' : 'FAIL'}</strong> {render()} </div> ))} </div> );}Promise Streaming with use()
Section titled “Promise Streaming with use()”The server creates a promise (resolves in 2s) and passes it as a prop through a parent async component that suspends for 3s. When the parent reveals at ~3s, the promise is already resolved — so use() returns instantly with no inner fallback. The elapsed time should be ~3000ms (the parent’s delay), not ~5000ms (which would mean the promise restarted on the client).
import { Suspense } from 'react';import SlowParent from './SlowParent';import UserCard from './UserCard';
async function fetchUser() { await new Promise(resolve => setTimeout(resolve, 2000)); return { name: 'Alice', role: 'Engineer' };}
function now() { return Date.now();}
export default function App() { const serverTime = now(); const userPromise = fetchUser(); return ( <div> <h1>Promise Streaming</h1> <p>Promise resolves in 2s. Parent suspends for 3s.</p> <Suspense fallback={<p>Outer: waiting for parent (3s)...</p>}> <SlowParent> <Suspense fallback={<p>Inner: waiting for data (should not appear!)</p>}> <UserCard userPromise={userPromise} serverTime={serverTime} /> </Suspense> </SlowParent> </Suspense> </div> );}export default async function SlowParent({ children }) { await new Promise(resolve => setTimeout(resolve, 3000)); return <div>{children}</div>;}'use client';import { use } from 'react';
function now() { return Date.now();}export default function UserCard({ userPromise, serverTime }) { const user = use(userPromise); const elapsed = now() - serverTime; return ( <div style={{ border: '1px solid #ccc', borderRadius: 8, padding: 16, }}> <strong>{user.name}</strong> <p>{user.role}</p> <p style={{ fontSize: 13 }}> Rendered {elapsed}ms after server created the promise. </p> <p style={{ color: '#666', fontSize: 12 }}> ~3000ms = promise already resolved, waited only for parent. ~5000ms would mean the promise restarted on the client. </p> </div> );}Flight Data Types in Server Actions
Section titled “Flight Data Types in Server Actions”This demo sends Map, Set, Date, and BigInt from a client component to a server action via encodeReply/decodeReply, then verifies the types survived the round trip.
import { testTypes, getResults } from './actions';import TestButton from './TestButton';
export default async function App() { const results = await getResults(); return ( <div> <h1>Flight Types in Server Actions</h1> <TestButton testTypes={testTypes} /> {results ? ( <div> {results.map(r => ( <div key={r.label} style={{ marginBottom: 12 }}> <strong>{r.label}: {r.ok ? 'pass' : 'FAIL'}</strong> <p>{r.detail}</p> </div> ))} </div> ) : ( <p>Click the button to send typed data to the server action.</p> )} </div> );}'use server';
let results = null;
export async function testTypes(map, set, date, big) { results = [ { label: 'Map', ok: map instanceof Map, detail: map instanceof Map ? 'entries: ' + JSON.stringify([...map.entries()]) : 'received: ' + typeof map, }, { label: 'Set', ok: set instanceof Set, detail: set instanceof Set ? 'values: ' + JSON.stringify([...set]) : 'received: ' + typeof set, }, { label: 'Date', ok: date instanceof Date, detail: date instanceof Date ? date.toISOString() : 'received: ' + typeof date, }, { label: 'BigInt', ok: typeof big === 'bigint', detail: typeof big === 'bigint' ? big.toString() : 'received: ' + typeof big, }, ];}
export async function getResults() { return results;}'use client';import { useTransition } from 'react';
export default function TestButton({ testTypes }) { const [pending, startTransition] = useTransition();
function handleClick() { startTransition(async () => { await testTypes( new Map([['alice', 100], ['bob', 200]]), new Set(['react', 'next', 'remix']), new Date('2025-06-15T12:00:00Z'), 9007199254740993n ); }); }
return ( <button onClick={handleClick} disabled={pending}> {pending ? 'Sending...' : 'Send typed data to server'} </button> );}Server Action Mutation + Re-render
Section titled “Server Action Mutation + Re-render”The server action mutates server-side data and returns a confirmation string. The updated list is only visible because the framework automatically re-renders the entire server component tree after the action completes — the server component re-reads the data and streams the new UI to the client.
import { getTodos } from './db';import { createTodo } from './actions';import AddTodo from './AddTodo';
export default function App() { const todos = getTodos(); return ( <div> <h1>Todo List</h1> <p style={{ color: '#666', fontSize: 13 }}> This list is rendered by a server component reading server-side data. It only updates because the server re-renders after each action. </p> <ul> {todos.map((todo, i) => ( <li key={i}>{todo}</li> ))} </ul> <AddTodo createTodo={createTodo} /> </div> );}let todos = ['Buy groceries'];
export function getTodos() { return [...todos];}
export function addTodo(text) { todos.push(text);}'use server';import { addTodo } from './db';
export async function createTodo(text) { if (!text) return 'Please enter a todo.'; addTodo(text); return 'Added: ' + text;}'use client';import { useState, useTransition } from 'react';
export default function AddTodo({ createTodo }) { const [text, setText] = useState(''); const [message, setMessage] = useState(''); const [pending, startTransition] = useTransition();
function handleSubmit(e) { e.preventDefault(); startTransition(async () => { const result = await createTodo(text); setMessage(result); setText(''); }); }
return ( <div> <form onSubmit={handleSubmit}> <input value={text} onChange={e => setText(e.target.value)} placeholder="New todo" /> <button disabled={pending}> {pending ? 'Adding...' : 'Add'} </button> </form> {message && ( <p style={{ color: '#666', fontSize: 13 }}> Action returned: "{message}" </p> )} </div> );}Inline Server Actions
Section titled “Inline Server Actions”Server actions defined inline inside a server component with 'use server' on the function body. The action closes over module-level state and is passed as a prop — no separate actions.js file needed.
import LikeButton from './LikeButton';
let count = 0;
export default function App() { async function addLike() { 'use server'; count++; }
return ( <div> <h1>Inline Server Actions</h1> <p>Likes: {count}</p> <LikeButton addLike={addLike} /> </div> );}'use client';
export default function LikeButton({ addLike }) { return ( <form action={addLike}> <button type="submit">Like</button> </form> );}Server Functions
Section titled “Server Functions”import { addLike, getLikeCount } from './actions';import LikeButton from './LikeButton';
export default async function App() { const count = await getLikeCount(); return ( <div> <h1>Server Functions</h1> <p>Likes: {count}</p> <LikeButton addLike={addLike} /> </div> );}'use server';
let count = 0;
export async function addLike() { count++;}
export async function getLikeCount() { return count;}'use client';
export default function LikeButton({ addLike }) { return ( <form action={addLike}> <button type="submit">Like</button> </form> );}