Open23

TanStack Start を久々に試す on Cloudflare

薄田達哉 / tatsuyasusukida薄田達哉 / tatsuyasusukida

このスクラップについて

Cloudflare で React Router v7 を使うのが疲れてきたので TanStack Start だったらどんな感じなのかを試してみる。

薄田達哉 / tatsuyasusukida薄田達哉 / tatsuyasusukida

まずは普通に TanStack Start を使う

https://tanstack.com/start/latest/docs/framework/react/build-from-scratch

コマンド
cd ~/workspace
mkdir scrap-tanstack-start
cd scrap-tanstack-start
npm init -y
touch tsconfig.json
pnpm i @tanstack/react-start @tanstack/react-router vite
pnpm i react react-dom
pnpm i -D typescript @types/react @types/react-dom vite-tsconfig-paths
touch vite.config.ts
tsconfig.json
{
  "compilerOptions": {
    "jsx": "react-jsx",
    "moduleResolution": "Bundler",
    "module": "ESNext",
    "target": "ES2022",
    "skipLibCheck": true,
    "strictNullChecks": true
  }
}
package.json(追記変更分)
{
  "type": "module",
  "scripts": {
    "dev": "vite dev",
    "build": "vite build"
  }
}

次回は Add the Basic Templating から始める。

https://tanstack.com/start/latest/docs/framework/react/build-from-scratch#add-the-basic-templating

薄田達哉 / tatsuyasusukida薄田達哉 / tatsuyasusukida

続き

コマンド
mkdir -p src/routes
touch src/router.tsx
touch src/routes/__root.tsx
src/router.tsx
import { createRouter as createTanStackRouter } from "@tanstack/react-router";
import { routeTree } from "./routeTree.gen";

export function createRouter() {
  const router = createTanStackRouter({
    routeTree,
    scrollRestoration: true,
  });

  return router;
}

declare module "@tanstack/react-router" {
  interface Register {
    router: ReturnType<typeof createRouter>;
  }
}
src/routes/__root.tsx
import {
  createRootRoute,
  HeadContent,
  Outlet,
  Scripts,
} from "@tanstack/react-router";
import type { ReactNode } from "react";

export const Route = createRootRoute({
  head: () => ({
    meta: [
      {
        charSet: "utf-8",
      },
      {
        name: "viewport",
        content: "width=device-width, initial-scale=1",
      },
      {
        title: "TanStack Start Starter",
      },
    ],
  }),
  component: RootComponent,
});

function RootComponent() {
  return (
    <RootDocument>
      <Outlet />
    </RootDocument>
  );
}

function RootDocument({ children }: Readonly<{ children: ReactNode }>) {
  return (
    <html lang="en">
      <head>
        <HeadContent />
      </head>
      <body>
        {children}
        <Scripts />
      </body>
    </html>
  );
}
薄田達哉 / tatsuyasusukida薄田達哉 / tatsuyasusukida

最初のルート

コマンド
touch src/routes/index.tsx
src/routes/index.tsx
import * as fs from "node:fs";
import { createFileRoute, useRouter } from "@tanstack/react-router";
import { createServerFn } from "@tanstack/react-start";

const filePath = "count.txt";

async function readCount() {
  return parseInt(
    await fs.promises.readFile(filePath, "utf-8").catch(() => "0"),
  );
}

const getCount = createServerFn({
  method: "GET",
}).handler(() => {
  return readCount();
});

const updateCount = createServerFn({
  method: "POST",
})
  .validator((d: number) => d)
  .handler(async ({ data }) => {
    const count = await readCount();
    await fs.promises.writeFile(filePath, `${count + data}`);
  });

export const Route = createFileRoute("/")({
  component: Home,
  loader: async () => await getCount(),
});

function Home() {
  const router = useRouter();
  const state = Route.useLoaderData();

  return (
    <button
      type="button"
      onClick={() => {
        updateCount({ data: 1 }).then(() => {
          router.invalidate();
        });
      }}
    >
      Add 1 to {state}?
    </button>
  );
}

Add 1 to 0? ボタンが表示され、クリックすると Add 1 to 1? ボタンに変化した。

また、count.txt ファイルが生成され、内容が 1 であることを確認した。

薄田達哉 / tatsuyasusukida薄田達哉 / tatsuyasusukida

次回は TanStack Start on Cloudflare をやっていこう

https://developers.cloudflare.com/workers/framework-guides/web-apps/tanstack/

薄田達哉 / tatsuyasusukida薄田達哉 / tatsuyasusukida

そらさん、コメントありがとうございます!
まだ乗せられていませんが、おかげさまでモチベーションが再燃したので再開しようと思います!

そらさんそらさん

すみません変なコメントを残してしまい!、、 
続きがすごく気になりますので、応援しています📣📣📣!

薄田達哉 / tatsuyasusukida薄田達哉 / tatsuyasusukida

ありがとうございます!とても励みになります!
最近はちょっとバタバタしているのですが 1 日 15 分だけでも進めてみようと思います!

薄田達哉 / tatsuyasusukida薄田達哉 / tatsuyasusukida

久々に再開してみる

そらさんからコメントをいただいたおかげでモチベーションが復活したので少しずつでも良いので進めていこう。

まずはリハビリに普通に TanStack Start を使うところから始めてみよう。

薄田達哉 / tatsuyasusukida薄田達哉 / tatsuyasusukida

次は Wrangler ファイルを追加する

その前に下記について軽く調べるところから始めよう、例えば下記は必須なのか?

  • observability
  • kv_namespaces
wrangler.jsonc
{
  "$schema": "node_modules/wrangler/config-schema.json",
  "name": "my-start-app",
  "main": ".output/server/index.mjs",
  "compatibility_date": "2025-09-04",
  "compatibility_flags": ["nodejs_compat"],
  "assets": {
    "directory": ".output/public"
  },
  "observability": {
    "enabled": true
  },
  "kv_namespaces": [
    {
      "binding": "CACHE",
      "id": "<Your KV ID>"
    }
  ]
}
薄田達哉 / tatsuyasusukida薄田達哉 / tatsuyasusukida

使用する wrangler.jsonc

AI に聞いたところ両方とも必須ではないが、observability は有効にしておいても良さそうだ。

というわけで kv_namespaces を削除したものを使用しよう。

wrangler.jsonc
{
  "$schema": "node_modules/wrangler/config-schema.json",
  "name": "my-start-app",
  "main": ".output/server/index.mjs",
  "compatibility_date": "2025-09-04",
  "compatibility_flags": ["nodejs_compat"],
  "assets": {
    "directory": ".output/public"
  },
  "observability": {
    "enabled": true
  }
}
薄田達哉 / tatsuyasusukida薄田達哉 / tatsuyasusukida

package.json へのコマンド追加

package.json(追記)
{
  "scripts": {
    ...
    "deploy": "pnpm build && wrangler deploy",
    "cf-typegen": "wrangler types --env-interface Env"
  }
}
薄田達哉 / tatsuyasusukida薄田達哉 / tatsuyasusukida

ビルドの実行

コマンド
pnpm build

エラー発生。

エラーメッセージ(抜粋)
[nitro 5:27:20]  ERROR  RollupError: node_modules/.pnpm/@tanstack+devtools@0.3.0_csstype@3.1.3_solid-js@1.9.9/node_modules/@tanstack/devtools/dist/esm/components/content-panel.js (1:19): "use" is not exported by "node_modules/.pnpm/solid-js@1.9.9/node_modules/solid-js/web/dist/server.js", imported by "node_modules/.pnpm/@tanstack+devtools@0.3.0_csstype@3.1.3_solid-js@1.9.9/node_modules/@tanstack/devtools/dist/esm/components/content-panel.js".


1: import { template, use, insert, memo, addEventListener, effect, className, delegateEvents } from "solid-js/web";
                      ^
2: import { useDevtoolsSettings } from "../context/use-devtools-context.js";
3: import { useStyles } from "../styles/use-styles.js";
薄田達哉 / tatsuyasusukida薄田達哉 / tatsuyasusukida

TanStack Devtools を無効にしてみた

src/routes/__root.tsx
import { HeadContent, Scripts, createRootRoute } from '@tanstack/react-router'
import { TanStackRouterDevtoolsPanel } from '@tanstack/react-router-devtools'
// import { TanstackDevtools } from '@tanstack/react-devtools'

import Header from '../components/Header'

import appCss from '../styles.css?url'

export const Route = createRootRoute({
  head: () => ({
    meta: [
      {
        charSet: 'utf-8',
      },
      {
        name: 'viewport',
        content: 'width=device-width, initial-scale=1',
      },
      {
        title: 'TanStack Start Starter',
      },
    ],
    links: [
      {
        rel: 'stylesheet',
        href: appCss,
      },
    ],
  }),

  shellComponent: RootDocument,
})

function RootDocument({ children }: { children: React.ReactNode }) {
  return (
    <html lang="en">
      <head>
        <HeadContent />
      </head>
      <body>
        <Header />
        {children}
        {/* <TanstackDevtools
          config={{
            position: 'bottom-left',
          }}
          plugins={[
            {
              name: 'Tanstack Router',
              render: <TanStackRouterDevtoolsPanel />,
            },
          ]}
        /> */}
        <Scripts />
      </body>
    </html>
  )
}

無事にビルドできたようだ。

コンソール出力
vite v6.3.5 building for production...
Generated route tree in 99ms
✓ 135 modules transformed.
.tanstack/start/build/client-dist/.vite/manifest.json                           1.75 kB │ gzip:  0.43 kB
.tanstack/start/build/client-dist/assets/logo-CHtJT8UQ.svg                      8.61 kB │ gzip:  3.65 kB
.tanstack/start/build/client-dist/assets/styles-CL-3Q1mh.css                   14.74 kB │ gzip:  3.56 kB
.tanstack/start/build/client-dist/assets/index-DcelEwDw.js                      0.83 kB │ gzip:  0.47 kB
.tanstack/start/build/client-dist/assets/demo.start.api-request-wXd1-3Wl.js     0.89 kB │ gzip:  0.53 kB
.tanstack/start/build/client-dist/assets/demo.start.server-funcs-BwrR4NrQ.js    1.73 kB │ gzip:  0.93 kB
.tanstack/start/build/client-dist/assets/main-LwwufoTX.js                     281.72 kB │ gzip: 89.17 kB
✓ built in 1.28s
vite v6.3.5 building SSR bundle for production...
✓ 113 modules transformed.
[plugin vite:css-post] Sourcemap is likely to be incorrect: a plugin (vite:css-post) was used to transform files, but didn't generate a sourcemap for the transformation. Consult the plugin documentation for help
✓ built in 493ms
✔ Generated public .output/public                               nitro 5:33:26
[nitro 5:33:27] ℹ Building Nitro Server (preset: cloudflare-module, compatibility date: 2024-11-19)
✔ Nitro Server built                                            nitro 5:33:30
  ├─ .output/server/chunks/_/_tanstack-start-manifest_v-DkvVKkKB.mjs (862 B) (348 B gzip)
  ├─ .output/server/chunks/_/_tanstack-start-manifest_v-DkvVKkKB.mjs.map (122 B) (120 B gzip)
  ├─ .output/server/chunks/_/_tanstack-start-server-fn-manifest_v-DXGikrzB.mjs (475 B) (251 B gzip)
  ├─ .output/server/chunks/_/_tanstack-start-server-fn-manifest_v-DXGikrzB.mjs.map (132 B) (127 B gzip)
  ├─ .output/server/chunks/_/demo.start.api-request-Ve_rBhMG.mjs (1.15 kB) (634 B gzip)
  ├─ .output/server/chunks/_/demo.start.api-request-Ve_rBhMG.mjs.map (797 B) (463 B gzip)
  ├─ .output/server/chunks/_/demo.start.server-funcs-BQ7ujM8Y.mjs (2.39 kB) (1.22 kB gzip)
  ├─ .output/server/chunks/_/demo.start.server-funcs-BQ7ujM8Y.mjs.map (2.04 kB) (1.04 kB gzip)
  ├─ .output/server/chunks/_/demo.start.server-funcs-C4TQEO_Z.mjs (2.71 kB) (1.29 kB gzip)
  ├─ .output/server/chunks/_/demo.start.server-funcs-C4TQEO_Z.mjs.map (2.33 kB) (1.14 kB gzip)
  ├─ .output/server/chunks/_/demo.start.server-funcs-D8396NO4.mjs (874 B) (497 B gzip)
  ├─ .output/server/chunks/_/demo.start.server-funcs-D8396NO4.mjs.map (976 B) (560 B gzip)
  ├─ .output/server/chunks/_/index-Dzbi00z0.mjs (1.08 kB) (571 B gzip)
  ├─ .output/server/chunks/_/index-Dzbi00z0.mjs.map (637 B) (369 B gzip)
  ├─ .output/server/chunks/_/ssr.mjs (374 kB) (107 kB gzip)
  ├─ .output/server/chunks/_/ssr.mjs.map (45.3 kB) (12.4 kB gzip)
  ├─ .output/server/chunks/nitro/nitro.mjs (81.9 kB) (27.2 kB gzip)
  ├─ .output/server/chunks/nitro/nitro.mjs.map (19.6 kB) (6.47 kB gzip)
  └─ .output/server/index.mjs (252 B) (188 B gzip)
Σ Total size: 538 kB (162 kB gzip)
ℹ Generated .output/public/_headers                                   5:33:30
[nitro 5:33:30] ✔ You can preview this build using npx wrangler dev .output/server/index.mjs --assets .output/public
[nitro 5:33:30] ✔ You can deploy this build using npx wrangler deploy .output/server/index.mjs --assets .output/public
[nitro 5:33:30] ✔ Client and Server bundles for TanStack Start have been successfully built.