Remix v2の概要
Remix v2は、Webの基礎技術(HTTP、フォーム、ブラウザキャッシュ)を活用したフルスタックReactフレームワークです。v2ではVite統合、型安全性の向上、パフォーマンス改善が実現しています。
Vite統合
設定
// vite.config.ts
import { vitePlugin as remix } from '@remix-run/dev';
import { defineConfig } from 'vite';
export default defineConfig({
plugins: [
remix({
future: {
v3_fetcherPersist: true,
v3_relativeSplatPath: true
}
})
]
});
開発サーバー
npm run dev
# Viteの高速HMRが利用可能
ファイルベースルーティング
app/
├── routes/
│ ├── _index.tsx # /
│ ├── about.tsx # /about
│ ├── posts._index.tsx # /posts
│ ├── posts.$postId.tsx # /posts/:postId
│ ├── posts.$postId_.edit.tsx # /posts/:postId/edit
│ └── ($lang).docs.$.tsx # /:lang?/docs/*
└── root.tsx
ルートの命名規則
| パターン | 意味 |
|---|---|
| _index | インデックスルート |
| $param | 動的パラメータ |
| ($param) | オプショナルパラメータ |
| $ | スプラット(キャッチオール) |
| _ | レイアウトなしのルート |
| . | ネストしたURL |
データローディング
loader
// routes/posts.$postId.tsx
import { json, type LoaderFunctionArgs } from '@remix-run/node';
import { useLoaderData } from '@remix-run/react';
export async function loader({ params }: LoaderFunctionArgs) {
const post = await db.post.findUnique({
where: { id: params.postId }
});
if (!post) {
throw new Response('Not Found', { status: 404 });
}
return json({ post });
}
export default function Post() {
const { post } = useLoaderData<typeof loader>();
return (
<article>
<h1>{post.title}</h1>
<p>{post.content}</p>
</article>
);
}
action
// routes/posts.new.tsx
import { redirect, type ActionFunctionArgs } from '@remix-run/node';
import { Form, useActionData } from '@remix-run/react';
import { z } from 'zod';
const schema = z.object({
title: z.string().min(1),
content: z.string().min(10)
});
export async function action({ request }: ActionFunctionArgs) {
const formData = await request.formData();
const result = schema.safeParse(Object.fromEntries(formData));
if (!result.success) {
return json({ errors: result.error.flatten() }, { status: 400 });
}
const post = await db.post.create({ data: result.data });
return redirect(`/posts/${post.id}`);
}
export default function NewPost() {
const actionData = useActionData<typeof action>();
return (
<Form method="post">
<input name="title" />
{actionData?.errors?.fieldErrors?.title && (
<span>{actionData.errors.fieldErrors.title}</span>
)}
<textarea name="content" />
<button type="submit">Create</button>
</Form>
);
}
fetcher
ナビゲーションなしでデータを送信・取得します。
import { useFetcher } from '@remix-run/react';
export default function LikeButton({ postId }: { postId: string }) {
const fetcher = useFetcher();
const isLiking = fetcher.state === 'submitting';
return (
<fetcher.Form method="post" action="/api/like">
<input type="hidden" name="postId" value={postId} />
<button type="submit" disabled={isLiking}>
{isLiking ? 'Liking...' : 'Like'}
</button>
</fetcher.Form>
);
}
defer と Suspense
import { defer } from '@remix-run/node';
import { Await, useLoaderData } from '@remix-run/react';
import { Suspense } from 'react';
export async function loader() {
// 即座に必要なデータ
const user = await getUser();
// 遅延読み込みするデータ
const recommendations = getRecommendations(); // awaitしない
return defer({
user,
recommendations
});
}
export default function Dashboard() {
const { user, recommendations } = useLoaderData<typeof loader>();
return (
<div>
<h1>Welcome, {user.name}</h1>
<Suspense fallback={<div>Loading recommendations...</div>}>
<Await resolve={recommendations}>
{(data) => <RecommendationsList items={data} />}
</Await>
</Suspense>
</div>
);
}
エラーハンドリング
// routes/posts.$postId.tsx
import { isRouteErrorResponse, useRouteError } from '@remix-run/react';
export function ErrorBoundary() {
const error = useRouteError();
if (isRouteErrorResponse(error)) {
return (
<div>
<h1>{error.status} {error.statusText}</h1>
<p>{error.data}</p>
</div>
);
}
return (
<div>
<h1>Error</h1>
<p>{error instanceof Error ? error.message : 'Unknown error'}</p>
</div>
);
}
メタデータ
import { type MetaFunction } from '@remix-run/node';
export const meta: MetaFunction<typeof loader> = ({ data }) => {
return [
{ title: data?.post.title ?? 'Not Found' },
{ name: 'description', content: data?.post.excerpt },
{ property: 'og:title', content: data?.post.title }
];
};
デプロイ
# Node.js
npm run build
npm start
# Cloudflare Pages
npm run build
npx wrangler pages deploy ./build/client
# Vercel
npm run build
# vercel.jsonで設定
まとめ
Remix v2は、Webの基礎技術を活用した堅牢なフルスタックフレームワークです。Vite統合による開発体験の向上、型安全なデータローディング、プログレッシブエンハンスメントにより、モダンなWebアプリケーションを効率的に構築できます。
← 一覧に戻る