К основному содержанию
скилл CX Codex

bun-react-ssr — скилл для Claude Code: React SSR на Bun

Скилл для сборки серверного React на Bun: renderToString, стриминг, гидратация и свой роутер без фреймворка.

Установить в Claude Code · Через Skills CLI (Vercel) Все способы и платформы ↓
npx skills add secondsky/claude-skills --skill bun-react-ssr -a claude-code
Открыть на GitHub

Что делает bun-react-ssr

Скилл подсказывает агенту, как собрать серверный рендеринг React на рантайме Bun. Внутри — готовые шаблоны: точка входа сервера через Bun.serve, клиентская гидратация через hydrateRoot, сборка бандла через Bun.build. Есть вариант со стримингом: renderToReadableStream и границы Suspense, чтобы оболочка отдавалась сразу.

Отдельный блок про данные: запрос к bun:sqlite, передача результата в компонент и проброс через window.__INITIAL_DATA__ на клиент. Роутинг тоже свой — небольшой компонент на useState и popstate, плюс Link с history.pushState. Для стилей два пути: вставить CSS в <style> или собрать отдельный файл и подключить ссылкой.

Проект придётся инициализировать самому: bun init, затем bun add react react-dom и типы для них. Скилл про кастомную схему, поэтому готовые решения вроде Next.js тут не при чём — если нужен фреймворк с файловым роутингом, это другой инструмент.

Какие задачи решает

  • поднять серверный рендеринг React на Bun без фреймворка
  • настроить стриминг HTML с границами Suspense
  • передать данные из SQLite в компонент и на клиент
  • собрать клиентский бандл через Bun.build
  • сделать простой роутер на popstate

Примеры запросов агенту

  • собери SSR-сервер на Bun с renderToString и гидратацией
  • переделай рендер на renderToReadableStream со стримингом
  • подключи bun:sqlite и пробрось данные в window.__INITIAL_DATA__

Как установить bun-react-ssr

1Через Skills CLI (Vercel)

npx skills add secondsky/claude-skills --skill bun-react-ssr -a claude-code

2Вручную: положить файлы

~/.claude/skills/bun-react-ssr/SKILL.md

Папка скилла целиком; для одного проекта — .claude/skills/bun-react-ssr/

По шагам, с командами для каждой платформы: Как установить скилл в Claude Code: пять способов

Из того же набора89

api-versioning-strategy Помогает выбрать способ версионирования API и провести старую версию до отключения без сюрпризов для клиентов. CX Codex +1 399установок bun-hot-reloading Скилл подсказывает агенту, как настроить горячую перезагрузку в Bun: режимы --watch и --hot, HMR и перезапуск HTTP-сервера. CX Codex +1 395установок claude-hook-writer Скилл-наставник по хукам Claude: проверяет дизайн, подсказывает безопасные паттерны и разбирает частые промахи. CX Codex +1 397установок logging-best-practices Скилл задаёт агенту правила структурного логирования: уровни, контекст запроса, маскировка персональных данных. Пригодится, когда логи пухнут или в них утекают пароли. CX Codex +1 396установок cloudflare-manager Скилл управляет аккаунтом Cloudflare из чата: разворачивает Workers, создаёт KV и R2, публикует Pages, настраивает DNS. CX Codex +1 393установок plan-interview Превращает черновой план в готовую к работе спецификацию через адаптивное интервью. Пригодится, когда идея есть, а требований пока нет. CX Codex +1 395установок bun-websocket-server Скилл с готовыми паттернами для WebSocket-сервера на Bun: обработчики, pub/sub, broadcast. Пригодится, когда нужен чат или стриминг в реальном времени. CX Codex +1 394установок bun-shell Скилл для работы с shell-скриптами на Bun: шаблонные литералы Bun.$, Bun.spawn, управление процессами и выводом. CX Codex +1 394установок

Похожие

bun brianlovin Переключает агента на среду Bun вместо привычных Node.js и npm. Настраивает команды запуска, встроенные базы данных и сервер без внешних пакетов. CX Codex +1 2 800установок streaming-ssr patterns.dev Учит агента отдавать HTML чанками через renderToPipeableStream и Suspense. Пригодится, когда страница большая и ждать полной отрисовки перед первым байтом нельзя. CX Codex +1 883установок react-start TanStack Правила и шаблоны для проектов на TanStack Start с React. Помогает агенту писать изоморфный код, настраивать Vite и связывать серверные функции с UI. CX Codex +1 454установок angular-ssr analogjs Скилл для задач Angular SSR, гидратации и пререндеринга. Он устарел: текущая версия больше не поддерживается. CX Codex +1 3 900установок bun-runtime affaan-m Скилл объясняет агенту, когда брать Bun вместо Node, и даёт команды для установки, тестов и деплоя на Vercel. CX Codex +1 4 700установок bun-development sickn33 Скилл помогает вести JavaScript и TypeScript-разработку на Bun: создавать проекты, переносить код с Node.js и разбирать проблемы рантайма. CX Codex +1 2 800установок

Рядом в теме «Фронтенд и UI»

Всё по теме «Фронтенд и UI» →

Вопросы о bun-react-ssr

Как установить bun-react-ssr в Claude?

Выполните npx skills add secondsky/claude-skills --skill bun-react-ssr -a claude-code. После этого файл скилла появится по пути ~/.claude/skills/bun-react-ssr/SKILL.md.

Что делает bun-react-ssr?

Даёт агенту шаблоны для серверного React на Bun: рендер строкой, стриминг, гидратация, сборка бандла, данные из SQLite и простой роутер.

Нужен ли фреймворк вроде Next.js?

Нет, скилл как раз про кастомную схему без фреймворка. Сервер, роутинг и сборку вы описываете сами по шаблонам из скилла.

Исходный текст

SKILL.md · MIT
---
name: bun-react-ssr
description: Use when building server-rendered React with Bun, including streaming SSR, hydration, renderToString, or custom SSR without a framework.
metadata:
  version: "1.0.0"
license: MIT
---

# 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({
  entrypoints: ["./src/styles.css"],
  outdir: "./public/static",
});

// Link in HTML
`<link rel="stylesheet" href="/static/styles.css">`
```

## Development Setup

### Hot Reload Development

```typescript
// dev.ts
import { watch } from "fs";

const srcDir = "./src";
let serverProcess: Subprocess | null = null;

async function startServer() {
  serverProcess?.kill();
  serverProcess = Bun.spawn(["bun", "run", "src/server.tsx"], {
    stdout: "inherit",
    stderr: "inherit",
  });
}

// Watch for changes
watch(srcDir, { recursive: true }, async (event, filename) => {
  console.log(`Change detected: ${filename}`);
  await startServer();
});

await startServer();
console.log("Dev server watching...");
```

## Production Build

```typescript
// build-prod.ts

// Build client
await Bun.build({
  entrypoints: ["./src/client.tsx"],
  outdir: "./dist/public/static",
  target: "browser",
  minify: true,
  splitting: true,
  sourcemap: "external",
});

// Build server
await Bun.build({
  entrypoints: ["./src/server.tsx"],
  outdir: "./dist",
  target: "bun",
  minify: true,
});

console.log("Build complete!");
```

## Common Errors

| Error | Cause | Fix |
|-------|-------|-----|
| `Hydration mismatch` | Server/client HTML differs | Check initial state |
| `document is not defined` | SSR accessing DOM | Guard with typeof window |
| `Cannot use hooks` | Hooks outside component | Check component structure |
| `Flash of unstyled content` | CSS not loaded | Inline critical CSS |

## Performance Tips

1. **Use streaming SSR** for faster TTFB
2. **Inline critical CSS** above the fold
3. **Code split** client bundle
4. **Cache rendered HTML** for static pages
5. **Use Suspense** for progressive loading

## When to Load References

Load `references/streaming-patterns.md` when:
- Complex Suspense boundaries
- Selective hydration
- Progressive enhancement

Load `references/caching.md` when:
- HTML caching strategies
- CDN integration
- Edge rendering

© secondsky · MIT · secondsky/claude-skills

Смотрите также

Спонсоры проекта

Компании, которые помогают держать аналитику открытой