Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ firebase.json
template.json
samconfig.toml
.docker
examples/*/dist
*.pem
*.sqlite
*.log
Expand Down
1 change: 1 addition & 0 deletions docs/public/html5.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
61 changes: 41 additions & 20 deletions docs/src/components/AdapterGrid.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import DenoIcon from "../../public/deno.svg?react";
import AzureIcon from "../../public/azure.svg?react";
import FirebaseIcon from "../../public/firebase.svg?react";
import DockerIcon from "../../public/docker.svg?react";
import Html5Icon from "../../public/html5.svg?react";

const adapters = [
{
Expand Down Expand Up @@ -109,32 +110,52 @@ const adapters = [
ja: "Node.js、Bun、または Deno コンテナ",
},
},
{
name: "Singlefile",
href: "/deploy/singlefile",
icon: Html5Icon,
color: "#E34F26",
description: {
en: "Self-contained single HTML file",
ja: "自己完結型の単一HTMLファイル",
},
experimental: true,
},
];

export default function AdapterGrid() {
const lang = useLanguage();
return (
<div className="my-4 grid grid-cols-1 md:grid-cols-3 auto-rows-fr gap-4 not-prose">
{adapters.map(({ name, href, icon: Icon, color, description }) => (
<a
key={href}
href={`${lang === defaultLanguage ? "" : `/${lang}`}${href}`}
hrefLang={lang}
className="adapter-card flex flex-col rounded-xl p-4 bg-gray-50 dark:bg-gray-800 text-xs shadow-lg dark:shadow-[rgba(255,255,255,0.1)] border border-gray-500 no-underline hover:no-underline transition-colors hover:border-gray-300 dark:hover:border-gray-400"
style={{ textDecoration: "none", color: "inherit" }}
>
<span className="adapter-card-title relative inline-flex items-start gap-2 self-start font-semibold text-base text-black dark:text-gray-300 mb-1">
<Icon
className="h-[1em] w-auto shrink-0 translate-y-[0.15em]"
style={color ? { color } : undefined}
/>
{name}
</span>
<span className="font-normal text-sm text-gray-500 dark:text-gray-400">
{description[lang] ?? description.en}
</span>
</a>
))}
{adapters.map(
({ name, href, icon: Icon, color, description, experimental }) => (
<a
key={href}
href={`${lang === defaultLanguage ? "" : `/${lang}`}${href}`}
hrefLang={lang}
className="adapter-card relative flex flex-col rounded-xl p-4 bg-gray-50 dark:bg-gray-800 text-xs shadow-lg dark:shadow-[rgba(255,255,255,0.1)] border border-gray-500 no-underline hover:no-underline transition-colors hover:border-gray-300 dark:hover:border-gray-400"
style={{ textDecoration: "none", color: "inherit" }}
>
{experimental ? (
<span className="absolute top-2 right-2 inline-flex items-center rounded-md bg-yellow-50 dark:bg-yellow-900/30 px-1.5 py-0.5 text-[10px] font-medium text-yellow-800 dark:text-yellow-200 ring-1 ring-inset ring-yellow-600/20">
Experimental
</span>
) : null}
<span className="adapter-card-title relative inline-flex items-start gap-2 self-start font-semibold text-base text-black dark:text-gray-300 mb-1">
{Icon ? (
<Icon
className="h-[1em] w-auto shrink-0 translate-y-[0.15em]"
style={color ? { color } : undefined}
/>
) : null}
{name}
</span>
<span className="font-normal text-sm text-gray-500 dark:text-gray-400">
{description[lang] ?? description.en}
</span>
</a>
)
)}
</div>
);
}
2 changes: 1 addition & 1 deletion docs/src/pages/en/(pages)/deploy/adapters.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ You can use adapters to configure your app for different deployment environments
## Configuration
</Link>

Add `adapter` entry to your `react-server.config.mjs` file. You can specify the name of a built-in adapter (`vercel`, `netlify`, `cloudflare`, `aws`, `bun`, `deno`, `azure`, `azure-swa`, `firebase`, or `docker`) as a string, or use an external adapter package.
Add `adapter` entry to your `react-server.config.mjs` file. You can specify the name of a built-in adapter (`vercel`, `netlify`, `cloudflare`, `aws`, `bun`, `deno`, `azure`, `azure-swa`, `firebase`, `docker`, or `singlefile`) as a string, or use an external adapter package.

> **Note:** When running a production build with **Bun** or **Deno**, the corresponding adapter is automatically detected and used without any configuration. You can override this with an explicit `adapter` setting in your config or via `--adapter <name>` on the CLI. Use `--no-adapter` to disable auto-detection.

Expand Down
95 changes: 95 additions & 0 deletions docs/src/pages/en/(pages)/deploy/singlefile.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
---
title: Singlefile
category: Deploy
order: 9
---

import Link from "../../../../components/Link.jsx";

# Singlefile

> **Experimental:** This adapter is experimental and may change in future releases. It is intended for simple, single-page applications and static sites.

The `singlefile` adapter bundles your entire statically exported React application into a single, self-contained HTML file. All CSS and JavaScript modules are inlined — no external resources are fetched at runtime.

This is useful for:

- **Offline-capable apps** that work from a `file://` URL or a single HTTP request
- **Portable demos** or prototypes you can share as a single file
- **Embedding** in environments where only one HTML file is allowed (e.g. email attachments, embedded webviews)

<Link name="installation">
## Installation
</Link>

No additional packages are needed — the adapter is built into `@lazarv/react-server`.

Add the adapter to your `react-server.config.mjs` file:

```mjs
export default {
adapter: "singlefile",
};
```

Or pass it via the CLI:

```sh
pnpm react-server build ./src/index.jsx --adapter singlefile
```

<Link name="how-it-works">
## How it works
</Link>

The singlefile adapter performs these steps during the build:

1. **Static export** — The `"/"` route is statically exported to produce `index.html`
2. **CSS inlining** — All `<link rel="stylesheet">` tags are replaced with inline `<style>` blocks. CSS references in React Server Component flight data are converted to `data:` URIs
3. **JS module inlining** — All client-side ES modules are base64-encoded and embedded in a boot `<script>`. At runtime, they are decoded into blob URLs and wired up via a dynamic [import map](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/script/type/importmap)
4. **Cleanup** — Modulepreload hints, dev-only live-reload links, and the static import map are removed

The result is a single `dist/index.html` file that contains everything the app needs to render and hydrate.

<Link name="build">
## Build
</Link>

Build your application:

```sh
pnpm react-server build [root] --adapter singlefile
```

This produces a `dist/` directory containing a single file:

```
dist/
└── index.html # Self-contained HTML with all CSS + JS inlined
```

<Link name="usage">
## Usage
</Link>

The output file works in any of these ways:

```sh
# Open directly in a browser
open dist/index.html

# Serve with any static file server
npx serve dist

# Or with Python
python3 -m http.server 3000 --directory dist
```

<Link name="limitations">
## Limitations
</Link>

- **Single route only** — Only the `"/"` path is exported. Multi-page applications with file-based routing are not supported.
- **No server-side features** — Server actions, API routes, and dynamic server rendering are not available. The output is purely static.
- **File size** — All assets are inlined (with base64 encoding for JS modules), so the output file will be larger than the sum of individual files (~33% overhead for JS due to base64).
- **SPA mode recommended** — This adapter works best with single-page applications. Use it with client components and client-side routing.
2 changes: 1 addition & 1 deletion docs/src/pages/ja/(pages)/deploy/adapters.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import AdapterGrid from "../../../../components/AdapterGrid.jsx";
## 設定
</Link>

`react-server.config.mjs`ファイルに `adapter` エントリを追加します。ビルトインアダプタの名前(`vercel`、`netlify`、`cloudflare`、`aws`、`bun`、`deno`、`azure`、`azure-swa`、`firebase`、または `docker`)を文字列で指定するか、外部アダプタパッケージを使用できます。
`react-server.config.mjs`ファイルに `adapter` エントリを追加します。ビルトインアダプタの名前(`vercel`、`netlify`、`cloudflare`、`aws`、`bun`、`deno`、`azure`、`azure-swa`、`firebase`、`docker`、または `singlefile`)を文字列で指定するか、外部アダプタパッケージを使用できます。

> **Note:** **Bun** または **Deno** でプロダクションビルドを実行すると、対応するアダプタが自動的に検出・使用されます。設定は不要です。明示的な `adapter` 設定またはCLIの `--adapter <name>` で上書きできます。自動検出を無効にするには `--no-adapter` を使用してください。

Expand Down
95 changes: 95 additions & 0 deletions docs/src/pages/ja/(pages)/deploy/singlefile.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
---
title: Singlefile
category: Deploy
order: 9
---

import Link from "../../../../components/Link.jsx";

# Singlefile

> **実験的:** このアダプタは実験的であり、将来のリリースで変更される可能性があります。シンプルなシングルページアプリケーションや静的サイトを対象としています。

`singlefile` アダプタは、静的エクスポートされた React アプリケーション全体を単一の自己完結型 HTML ファイルにバンドルします。すべての CSS と JavaScript モジュールがインライン化され、実行時に外部リソースを取得する必要がありません。

以下のような用途に便利です:

- `file://` URL や単一の HTTP リクエストで動作する**オフライン対応アプリ**
- 単一ファイルとして共有できる**ポータブルなデモ**やプロトタイプ
- 1 つの HTML ファイルのみが許可される環境(メール添付、埋め込みウェブビューなど)での**埋め込み**

<Link name="installation">
## インストール
</Link>

追加のパッケージは不要です — アダプタは `@lazarv/react-server` に組み込まれています。

`react-server.config.mjs` ファイルにアダプタを追加します:

```mjs
export default {
adapter: "singlefile",
};
```

または CLI で指定します:

```sh
pnpm react-server build ./src/index.jsx --adapter singlefile
```

<Link name="how-it-works">
## 仕組み
</Link>

singlefile アダプタはビルド時に以下のステップを実行します:

1. **静的エクスポート** — `"/"` ルートを静的エクスポートして `index.html` を生成します
2. **CSS のインライン化** — すべての `<link rel="stylesheet">` タグをインライン `<style>` ブロックに置換します。React Server Component のフライトデータ内の CSS 参照は `data:` URI に変換されます
3. **JS モジュールのインライン化** — すべてのクライアント側 ES モジュールを base64 エンコードしてブート `<script>` に埋め込みます。実行時にデコードされ、blob URL として動的な [import map](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/script/type/importmap) を通じて接続されます
4. **クリーンアップ** — modulepreload ヒント、開発用ライブリロードリンク、静的 import map が削除されます

結果として、アプリのレンダリングとハイドレーションに必要なすべてを含む単一の `dist/index.html` ファイルが生成されます。

<Link name="build">
## ビルド
</Link>

アプリケーションをビルドします:

```sh
pnpm react-server build [root] --adapter singlefile
```

単一ファイルを含む `dist/` ディレクトリが生成されます:

```
dist/
└── index.html # CSS + JS がすべてインライン化された自己完結型 HTML
```

<Link name="usage">
## 使い方
</Link>

出力ファイルは以下のいずれかの方法で使用できます:

```sh
# ブラウザで直接開く
open dist/index.html

# 任意の静的ファイルサーバーで配信
npx serve dist

# または Python で配信
python3 -m http.server 3000 --directory dist
```

<Link name="limitations">
## 制限事項
</Link>

- **単一ルートのみ** — `"/"` パスのみがエクスポートされます。ファイルベースルーティングを使用するマルチページアプリケーションはサポートされていません。
- **サーバー側機能なし** — サーバーアクション、API ルート、動的サーバーレンダリングは利用できません。出力は純粋に静的です。
- **ファイルサイズ** — すべてのアセットがインライン化されるため(JS モジュールは base64 エンコード)、出力ファイルは個別ファイルの合計より大きくなります(JS は base64 により約 33% のオーバーヘッド)。
- **SPA モード推奨** — このアダプタはシングルページアプリケーションに最適です。クライアントコンポーネントとクライアント側ルーティングと併用してください。
Loading