Skip to main content
ClaudeWave
Skill169 estrellas del repoactualizado 29d ago

bun-react-ssr

Use when building server-rendered React with Bun, including streaming SSR, hydration, renderToString, or custom SSR without a framework.

Instalar en Claude Code
Copiar
git clone --depth 1 https://github.com/secondsky/claude-skills /tmp/bun-react-ssr && cp -r /tmp/bun-react-ssr/plugins/bun/skills/bun-react-ssr ~/.claude/skills/bun-react-ssr
Después abre una sesión nueva de Claude Code; el skill carga automáticamente.

SKILL.md

# Bun React SSR

Build custom server-rendered React applications with Bun.

## Quick Start

```bash
# Initialize project
mkdir my-ssr-app && cd my-ssr-app
bun init

# Install dependencies
bun add react react-dom
bun add -D @types/react @types/react-dom
```

## Basic SSR Setup

### Server Entry

```typescript
// src/server.tsx
import { renderToString } from "react-dom/server";
import App from "./App";

Bun.serve({
  port: 3000,
  async fetch(req) {
    const url = new URL(req.url);

    // Serve static files
    if (url.pathname.startsWith("/static/")) {
      const file = Bun.file(`./public${url.pathname}`);
      if (await file.exists()) {
        return new Response(file);
      }
    }

    // Render React app
    const html = renderToString(<App url={url.pathname} />);

    return new Response(
      `<!DOCTYPE html>
      <html>
        <head>
          <meta charset="utf-8">
          <meta name="viewport" content="width=device-width, initial-scale=1">
          <title>React SSR</title>
        </head>
        <body>
          <div id="root">${html}</div>
          <script src="/static/client.js"></script>
        </body>
      </html>`,
      {
        headers: { "Content-Type": "text/html" },
      }
    );
  },
});

console.log("Server running on http://localhost:3000");
```

### Client Entry

```tsx
// src/client.tsx
import { hydrateRoot } from "react-dom/client";
import App from "./App";

hydrateRoot(
  document.getElementById("root")!,
  <App url={window.location.pathname} />
);
```

### React App

```tsx
// src/App.tsx
interface AppProps {
  url: string;
}

export default function App({ url }: AppProps) {
  return (
    <div>
      <h1>React SSR with Bun</h1>
      <p>Current path: {url}</p>
      <button onClick={() => alert("Hydrated!")}>Click me</button>
    </div>
  );
}
```

## Build Client Bundle

```typescript
// build.ts
await Bun.build({
  entrypoints: ["./src/client.tsx"],
  outdir: "./public/static",
  target: "browser",
  minify: true,
  splitting: true,
});
```

```bash
# Build client
bun run build.ts

# Start server
bun run src/server.tsx
```

## Streaming SSR

```tsx
// src/server-streaming.tsx
import { renderToReadableStream } from "react-dom/server";
import App from "./App";

Bun.serve({
  port: 3000,
  async fetch(req) {
    const url = new URL(req.url);

    const stream = await renderToReadableStream(
      <App url={url.pathname} />,
      {
        bootstrapScripts: ["/static/client.js"],
        onError(error) {
          console.error(error);
        },
      }
    );

    // Wait for shell to be ready (Suspense boundaries)
    await stream.allReady;

    return new Response(stream, {
      headers: { "Content-Type": "text/html" },
    });
  },
});
```

### With Suspense

```tsx
// src/App.tsx
import { Suspense } from "react";

function SlowComponent() {
  // This would be a data fetching component
  return <div>Loaded!</div>;
}

export default function App({ url }: { url: string }) {
  return (
    <html>
      <head>
        <title>Streaming SSR</title>
      </head>
      <body>
        <div id="root">
          <h1>Fast Shell</h1>
          <Suspense fallback={<div>Loading...</div>}>
            <SlowComponent />
          </Suspense>
        </div>
      </body>
    </html>
  );
}
```

## Data Fetching

### Server-Side Data

```tsx
// src/server.tsx
import { renderToString } from "react-dom/server";
import { Database } from "bun:sqlite";
import App from "./App";

const db = new Database("data.sqlite");

Bun.serve({
  async fetch(req) {
    const url = new URL(req.url);

    // Fetch data server-side
    const users = db.query("SELECT * FROM users").all();

    const html = renderToString(
      <App url={url.pathname} initialData={{ users }} />
    );

    return new Response(
      `<!DOCTYPE html>
      <html>
        <head><title>SSR</title></head>
        <body>
          <div id="root">${html}</div>
          <script>
            window.__INITIAL_DATA__ = ${JSON.stringify({ users })};
          </script>
          <script src="/static/client.js"></script>
        </body>
      </html>`,
      { headers: { "Content-Type": "text/html" } }
    );
  },
});
```

### Client Hydration

```tsx
// src/client.tsx
import { hydrateRoot } from "react-dom/client";
import App from "./App";

const initialData = (window as any).__INITIAL_DATA__;

hydrateRoot(
  document.getElementById("root")!,
  <App url={window.location.pathname} initialData={initialData} />
);
```

## Routing

### Simple Router

```tsx
// src/Router.tsx
import { useState, useEffect } from "react";

interface Route {
  path: string;
  component: React.ComponentType;
}

interface RouterProps {
  routes: Route[];
  initialPath: string;
}

export function Router({ routes, initialPath }: RouterProps) {
  const [path, setPath] = useState(initialPath);

  useEffect(() => {
    const handlePopState = () => setPath(window.location.pathname);
    window.addEventListener("popstate", handlePopState);
    return () => window.removeEventListener("popstate", handlePopState);
  }, []);

  const route = routes.find((r) => r.path === path);
  const Component = route?.component || NotFound;

  return <Component />;
}

export function Link({ href, children }: { href: string; children: React.ReactNode }) {
  const handleClick = (e: React.MouseEvent) => {
    e.preventDefault();
    window.history.pushState({}, "", href);
    window.dispatchEvent(new PopStateEvent("popstate"));
  };

  return <a href={href} onClick={handleClick}>{children}</a>;
}

function NotFound() {
  return <h1>404 - Not Found</h1>;
}
```

## CSS Handling

### Inline Styles

```tsx
const html = renderToString(<App />);

return new Response(
  `<!DOCTYPE html>
  <html>
    <head>
      <style>${await Bun.file("./src/styles.css").text()}</style>
    </head>
    <body>
      <div id="root">${html}</div>
    </body>
  </html>`,
  { headers: { "Content-Type": "text/html" } }
);
```

### External Stylesheet

```tsx
// Build CSS
await Bun.build(
access-control-rbacSkill

Role-based access control (RBAC) with permissions and policies. Use for admin dashboards, enterprise access, multi-tenant apps, fine-grained authorization, or encountering permission hierarchies, role inheritance, policy conflicts.

aceternity-uiSkill

100+ animated React components (Aceternity UI) for Next.js with Tailwind. Use for hero sections, parallax, 3D effects, or encountering animation, shadcn CLI integration errors.

ai-elements-chatbotSkill

shadcn/ui AI chat components for conversational interfaces. Use for streaming chat, tool/function displays, reasoning visualization, or encountering Next.js App Router setup, Tailwind v4 integration, AI SDK v5 migration errors.

ai-sdk-coreSkill

Vercel AI SDK v5 for backend AI (text generation, structured output, tools, agents). Multi-provider. Use for server-side AI or encountering AI_APICallError, AI_NoObjectGeneratedError, streaming failures.

ai-sdk-uiSkill

Vercel AI SDK v5 React hooks (useChat, useCompletion, useObject) for AI chat interfaces. Use for React/Next.js AI apps or encountering parse stream errors, no response, streaming issues.

api-authenticationSkill

Secure API authentication with JWT, OAuth 2.0, API keys. Use for authentication systems, third-party integrations, service-to-service communication, or encountering token management, security headers, auth flow errors.

api-changelog-versioningSkill

Creates comprehensive API changelogs documenting breaking changes, deprecations, and migration strategies for API consumers. Use when managing API versions, communicating breaking changes, or creating upgrade guides.

api-contract-testingSkill

Verifies API contracts between services using consumer-driven contracts, schema validation, and tools like Pact. Use when testing microservices communication, preventing breaking changes, or validating OpenAPI specifications.