From 3672ca2f8a8ce7905d8441ce5a888169f869cce3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Viktor=20L=C3=A1z=C3=A1r?= Date: Sat, 28 Feb 2026 12:48:40 +0100 Subject: [PATCH 1/2] docs: integrations update --- .../pages/en/(pages)/integrations/mantine.mdx | 190 ++++++++++++- .../src/pages/en/(pages)/integrations/mui.mdx | 166 +++++++++++- .../en/(pages)/integrations/react-query.mdx | 245 ++++++++++++++++- .../pages/ja/(pages)/integrations/mantine.mdx | 198 ++++++++++++++ .../src/pages/ja/(pages)/integrations/mui.mdx | 174 ++++++++++++ .../ja/(pages)/integrations/react-query.mdx | 253 ++++++++++++++++++ 6 files changed, 1220 insertions(+), 6 deletions(-) create mode 100644 docs/src/pages/ja/(pages)/integrations/mantine.mdx create mode 100644 docs/src/pages/ja/(pages)/integrations/mui.mdx create mode 100644 docs/src/pages/ja/(pages)/integrations/react-query.mdx diff --git a/docs/src/pages/en/(pages)/integrations/mantine.mdx b/docs/src/pages/en/(pages)/integrations/mantine.mdx index b34792ec..27cf9027 100644 --- a/docs/src/pages/en/(pages)/integrations/mantine.mdx +++ b/docs/src/pages/en/(pages)/integrations/mantine.mdx @@ -2,12 +2,198 @@ title: Mantine UI category: Integrations order: 5 -contents: false --- import Link from "../../../../components/Link.jsx"; # Mantine UI -> Coming soon. +`@lazarv/react-server` is compatible with [Mantine](https://mantine.dev/), a full-featured React component library. You can use Mantine components in your client components with server-side rendering support, PostCSS configuration for Mantine's styling system, and the `MantineProvider` for theming. + + +## Installation + + +Install Mantine and its required PostCSS dependencies: + +```sh +pnpm add @mantine/core @mantine/hooks +pnpm add -D postcss postcss-preset-mantine postcss-simple-vars +``` + + +## PostCSS configuration + + +Mantine requires a PostCSS configuration with the `postcss-preset-mantine` plugin and breakpoint variables: + +```js filename="postcss.config.cjs" +module.exports = { + plugins: { + "postcss-preset-mantine": {}, + "postcss-simple-vars": { + variables: { + "mantine-breakpoint-xs": "36em", + "mantine-breakpoint-sm": "48em", + "mantine-breakpoint-md": "62em", + "mantine-breakpoint-lg": "75em", + "mantine-breakpoint-xl": "88em", + }, + }, + }, +}; +``` + + +## Provider setup + + +Import the core Mantine styles and wrap your app with `MantineProvider` in your root layout. Include the `ColorSchemeScript` in the `` to prevent a flash of unstyled content: + +```tsx filename="src/pages/layout.tsx" +import "@mantine/core/styles.css"; + +import { ColorSchemeScript, createTheme, MantineProvider } from "@mantine/core"; + +const theme = createTheme({ + /** Put your mantine theme override here */ +}); + +export default function Layout({ children }: { children: React.ReactNode }) { + return ( + + + + + + + {children} + + + + ); +} +``` + + +## Using components + + +Mantine components that use React hooks or browser APIs must be used in client components. Create a client component and import the Mantine components you need: + +```tsx filename="src/components/Counter.tsx" +"use client"; + +import { useState } from "react"; +import { Button, Group, Text } from "@mantine/core"; + +export function Counter() { + const [count, setCount] = useState(0); + + return ( + + + {count} + + + ); +} +``` + +Then use the client component from your server component page: + +```tsx filename="src/pages/index.tsx" +import { Counter } from "../components/Counter"; + +export default function HomePage() { + return ; +} +``` + + +## Navigation + + +To use Mantine's navigation components like `NavLink` with `@lazarv/react-server`'s client-side navigation, pass the framework's `Link` component via the `component` prop: + +```tsx filename="src/components/MainNavigation.tsx" +"use client"; + +import { NavLink } from "@mantine/core"; +import { Link, usePathname } from "@lazarv/react-server/navigation"; + +export function MainNavigation() { + const pathname = usePathname(); + + return ( + <> + + + + ); +} +``` + + +## Extension packages + + +Mantine offers many extension packages for additional functionality. Import their styles in the pages or layouts where they are used: + +```tsx filename="src/pages/dates.tsx" +import "@mantine/dates/styles.css"; + +import { MyDates } from "../components/MyDates"; + +export default function DatesPage() { + return ; +} +``` + +Some commonly used Mantine extensions and their style imports: + +| Package | Style import | +|---------|-------------| +| `@mantine/dates` | `@mantine/dates/styles.css` | +| `@mantine/charts` | `@mantine/charts/styles.css` | +| `@mantine/notifications` | `@mantine/notifications/styles.css` | +| `@mantine/code-highlight` | `@mantine/code-highlight/styles.css` | +| `@mantine/carousel` | `@mantine/carousel/styles.css` | +| `@mantine/spotlight` | `@mantine/spotlight/styles.css` | +| `@mantine/nprogress` | `@mantine/nprogress/styles.css` | +| `@mantine/tiptap` | `@mantine/tiptap/styles.css` | +| `@mantine/dropzone` | `@mantine/dropzone/styles.css` | + + +## Client-only components + + +Some components (like charts that depend on browser APIs) cannot be server-side rendered. Use `ClientOnly` from `@lazarv/react-server/client` to render them only on the client: + +```tsx filename="src/pages/charts.tsx" +import "@mantine/charts/styles.css"; + +import { ClientOnly } from "@lazarv/react-server/client"; +import { MyAreaChart } from "../components/MyAreaChart"; + +export default function ChartsPage() { + return ( + + + + ); +} +``` + +> Check out the [Mantine example](https://github.com/lazarv/react-server/tree/main/examples/mantine) to see a complete example of using Mantine UI with `@lazarv/react-server`. diff --git a/docs/src/pages/en/(pages)/integrations/mui.mdx b/docs/src/pages/en/(pages)/integrations/mui.mdx index 5552dc58..c1e9c5b9 100644 --- a/docs/src/pages/en/(pages)/integrations/mui.mdx +++ b/docs/src/pages/en/(pages)/integrations/mui.mdx @@ -2,12 +2,174 @@ title: Material UI category: Integrations order: 6 -contents: false --- import Link from "../../../../components/Link.jsx"; # Material UI -> Coming soon. +`@lazarv/react-server` is compatible with [Material UI (MUI)](https://mui.com/material-ui/), a popular React component library implementing Google's Material Design. MUI uses Emotion for CSS-in-JS styling, which works out of the box with `@lazarv/react-server`. + + +## Installation + + +Install Material UI and its required dependencies: + +```sh +pnpm add @mui/material @emotion/react @emotion/styled +``` + +Optionally, install the Roboto font and Material Icons: + +```sh +pnpm add @fontsource/roboto @mui/icons-material +``` + + +## Theme setup + + +Create a theme file to configure your Material UI theme: + +```js filename="app/theme.mjs" +import { createTheme } from "@mui/material/styles"; + +const theme = createTheme({ + palette: { + mode: "light", + }, + typography: { + fontFamily: "Roboto", + }, +}); + +export default theme; +``` + + +## Provider setup + + +Material UI requires a `ThemeProvider` and `CssBaseline` for proper theming and baseline styles. Create a client component that wraps your app: + +```jsx filename="app/components/Layout.jsx" +"use client"; + +import Container from "@mui/material/Container"; +import CssBaseline from "@mui/material/CssBaseline"; +import { ThemeProvider } from "@mui/material/styles"; + +import theme from "../theme"; + +export default function Layout({ children }) { + return ( + + + + {children} + + + ); +} +``` + +Then set up your root layout to import the font and use the `Layout` provider component: + +```jsx filename="app/layout.jsx" +import "@fontsource/roboto/300.css"; +import "@fontsource/roboto/400.css"; +import "@fontsource/roboto/500.css"; +import "@fontsource/roboto/700.css"; + +import Layout from "./components/Layout"; + +export default function RootLayout({ children }) { + return ( + + + + + My App + + + {children} + + + ); +} +``` + + +## Navigation + + +To use MUI link and button components with `@lazarv/react-server`'s client-side navigation, pass the framework's `Link` component via the `component` prop: + +```jsx filename="app/page.jsx" +"use client"; + +import { Link as ReactServerLink } from "@lazarv/react-server/navigation"; +import Link from "@mui/material/Link"; + +export default function Home() { + return ( + + Go to the about page + + ); +} +``` + +The same pattern works with MUI `Button` for navigation: + +```jsx filename="app/about/page.jsx" +"use client"; + +import { Link as ReactServerLink } from "@lazarv/react-server/navigation"; +import Button from "@mui/material/Button"; + +export default function About() { + return ( + + ); +} +``` + + +## Server components + + +Some MUI components that don't rely on browser APIs or React hooks can be used directly in server components. Components like `Typography` and `Link` (without navigation) work in server components: + +```jsx filename="app/components/Copyright.jsx" +import Typography from "@mui/material/Typography"; +import Link from "@mui/material/Link"; + +export default function Copyright() { + return ( + + {"Copyright © "} + + Your Website + {" "} + {new Date().getFullYear()}. + + ); +} +``` + + +## Icons + + +When using `@mui/icons-material`, import icons from the ESM path for proper module resolution: + +```jsx +import LightbulbOutlined from "@mui/icons-material/esm/LightbulbOutlined"; +``` + +> Check out the [Material UI example](https://github.com/lazarv/react-server/tree/main/examples/mui) to see a complete example of using MUI with `@lazarv/react-server`. diff --git a/docs/src/pages/en/(pages)/integrations/react-query.mdx b/docs/src/pages/en/(pages)/integrations/react-query.mdx index bc4624a2..ca8fdcde 100644 --- a/docs/src/pages/en/(pages)/integrations/react-query.mdx +++ b/docs/src/pages/en/(pages)/integrations/react-query.mdx @@ -2,12 +2,253 @@ title: TanStack Query category: Integrations order: 4 -contents: false --- import Link from "../../../../components/Link.jsx"; # TanStack Query -> Coming soon. +`@lazarv/react-server` works with [TanStack Query](https://tanstack.com/query) (formerly React Query) to provide powerful data fetching with server-side prefetching and client-side hydration. You can prefetch queries in server components and seamlessly hydrate the data on the client, avoiding unnecessary re-fetches. + + +## Installation + + +Install TanStack Query in your project: + +```sh +pnpm add @tanstack/react-query +``` + + +## Setup + + +To use TanStack Query, you need to create a `QueryClient` and wrap your app in a `QueryClientProvider`. Since `QueryClientProvider` relies on React context, it must be a client component. + +Create a query client factory that handles both server and browser environments: + +```jsx filename="app/get-query-client.jsx" +import { + defaultShouldDehydrateQuery, + isServer, + QueryClient, +} from "@tanstack/react-query"; + +function makeQueryClient() { + return new QueryClient({ + defaultOptions: { + queries: { + // With SSR, set some default staleTime above 0 + // to avoid refetching immediately on the client + staleTime: 60 * 1000, + }, + dehydrate: { + // include pending queries in dehydration + shouldDehydrateQuery: (query) => + defaultShouldDehydrateQuery(query) || + query.state.status === "pending", + }, + }, + }); +} + +let browserQueryClient = undefined; + +export function getQueryClient() { + if (isServer) { + // Server: always make a new query client + return makeQueryClient(); + } else { + // Browser: make a new query client if we don't already have one + if (!browserQueryClient) browserQueryClient = makeQueryClient(); + return browserQueryClient; + } +} +``` + +Create a client component that provides the `QueryClient` to the rest of your app: + +```jsx filename="app/providers.jsx" +"use client"; + +import { + isServer, + QueryClient, + QueryClientProvider, +} from "@tanstack/react-query"; + +function makeQueryClient() { + return new QueryClient({ + defaultOptions: { + queries: { + staleTime: 60 * 1000, + }, + }, + }); +} + +let browserQueryClient = undefined; + +function getQueryClient() { + if (isServer) { + return makeQueryClient(); + } else { + if (!browserQueryClient) browserQueryClient = makeQueryClient(); + return browserQueryClient; + } +} + +export default function Providers({ children }) { + const queryClient = getQueryClient(); + + return ( + + {children} + + ); +} +``` + +Then wrap your app in the `Providers` component from your root layout: + +```jsx filename="app/layout.jsx" +import Providers from "./providers"; + +export default function RootLayout({ children }) { + return ( + + + + {children} + + + ); +} +``` + + +## Server-side prefetching + + +The key advantage of using TanStack Query with `@lazarv/react-server` is the ability to prefetch data in server components and hydrate it on the client. This means data is available instantly without a loading state. + +In a server component, use the query client to prefetch data and wrap your client components in a `HydrationBoundary`: + +```jsx filename="app/page.jsx" +import { dehydrate, HydrationBoundary } from "@tanstack/react-query"; + +import { getPosts } from "./get-posts"; +import { getQueryClient } from "./get-query-client"; +import Posts from "./posts"; + +export default function PostsPage() { + const queryClient = getQueryClient(); + + queryClient.prefetchQuery({ + queryKey: ["posts"], + queryFn: getPosts, + }); + + return ( + + + + ); +} +``` + + +## Client components + + +Client components can use `useSuspenseQuery` (or `useQuery`) with the same query keys. When the data was prefetched on the server, it will be immediately available without a loading state: + +```jsx filename="app/posts.jsx" +"use client"; + +import { useSuspenseQuery } from "@tanstack/react-query"; +import { getPosts } from "./get-posts"; + +export default function Posts() { + const { data } = useSuspenseQuery({ + queryKey: ["posts"], + queryFn: getPosts, + }); + + return ( +
    + {data.map((post) => ( +
  • {post.title}
  • + ))} +
+ ); +} +``` + + +## Isomorphic data fetching + + +To share data fetching logic between server and client, you can create isomorphic data fetchers that detect the runtime environment: + +```js filename="app/get-posts.mjs" +export async function getPosts() { + if (typeof document === "undefined") { + // Server: import data directly + const { default: posts } = await import("../data/posts.json"); + return posts; + } else { + // Client: fetch from API route + const res = await fetch("/api/posts"); + return res.json(); + } +} +``` + +You can combine this with API routes using file-system routing: + +```jsx filename="app/api/GET.posts.jsx" +import posts from "../../data/posts.json"; + +export default async function GET() { + return new Response(JSON.stringify(posts), { + status: 200, + headers: { + "Content-Type": "application/json", + }, + }); +} +``` + + +## Nested hydration boundaries + + +You can nest `HydrationBoundary` components in separate server components to prefetch different sets of data. This is useful for composing multiple data dependencies: + +```jsx filename="app/comments-server.jsx" +import { dehydrate, HydrationBoundary } from "@tanstack/react-query"; + +import Comments from "./comments"; +import { getComments } from "./get-comments"; +import { getQueryClient } from "./get-query-client"; + +export default function CommentsServerComponent() { + const queryClient = getQueryClient(); + + queryClient.prefetchQuery({ + queryKey: ["comments"], + queryFn: getComments, + }); + + return ( + + + + ); +} +``` + +> Check out the [TanStack Query example](https://github.com/lazarv/react-server/tree/main/examples/react-query) to see a complete example of using TanStack Query with `@lazarv/react-server`. diff --git a/docs/src/pages/ja/(pages)/integrations/mantine.mdx b/docs/src/pages/ja/(pages)/integrations/mantine.mdx new file mode 100644 index 00000000..e92488cb --- /dev/null +++ b/docs/src/pages/ja/(pages)/integrations/mantine.mdx @@ -0,0 +1,198 @@ +--- +title: Mantine UI +category: Integrations +order: 5 +--- + +import Link from "../../../../components/Link.jsx"; + +# Mantine UI + +`@lazarv/react-server`は、フル機能のReactコンポーネントライブラリである[Mantine](https://mantine.dev/)と互換性があります。クライアントコンポーネントでMantineコンポーネントを使用し、サーバーサイドレンダリングのサポート、MantineのスタイリングシステムのためのPostCSS設定、テーマのための`MantineProvider`を利用できます。 + + +## インストール + + +Mantineと必要なPostCSS依存関係をインストールします: + +```sh +pnpm add @mantine/core @mantine/hooks +pnpm add -D postcss postcss-preset-mantine postcss-simple-vars +``` + + +## PostCSS設定 + + +Mantineには`postcss-preset-mantine`プラグインとブレークポイント変数を含むPostCSS設定が必要です: + +```js filename="postcss.config.cjs" +module.exports = { + plugins: { + "postcss-preset-mantine": {}, + "postcss-simple-vars": { + variables: { + "mantine-breakpoint-xs": "36em", + "mantine-breakpoint-sm": "48em", + "mantine-breakpoint-md": "62em", + "mantine-breakpoint-lg": "75em", + "mantine-breakpoint-xl": "88em", + }, + }, + }, +}; +``` + + +## プロバイダーのセットアップ + + +コアMantineスタイルをインポートし、ルートレイアウトで`MantineProvider`でアプリをラップします。スタイルなしコンテンツのフラッシュを防ぐために、``に`ColorSchemeScript`を含めます: + +```tsx filename="src/pages/layout.tsx" +import "@mantine/core/styles.css"; + +import { ColorSchemeScript, createTheme, MantineProvider } from "@mantine/core"; + +const theme = createTheme({ + /** Mantineのテーマオーバーライドをここに記述 */ +}); + +export default function Layout({ children }: { children: React.ReactNode }) { + return ( + + + + + + + {children} + + + + ); +} +``` + + +## コンポーネントの使用 + + +ReactフックやブラウザAPIを使用するMantineコンポーネントは、クライアントコンポーネントで使用する必要があります。クライアントコンポーネントを作成し、必要なMantineコンポーネントをインポートします: + +```tsx filename="src/components/Counter.tsx" +"use client"; + +import { useState } from "react"; +import { Button, Group, Text } from "@mantine/core"; + +export function Counter() { + const [count, setCount] = useState(0); + + return ( + + + {count} + + + ); +} +``` + +次に、サーバーコンポーネントページからクライアントコンポーネントを使用します: + +```tsx filename="src/pages/index.tsx" +import { Counter } from "../components/Counter"; + +export default function HomePage() { + return ; +} +``` + + +## ナビゲーション + + +`@lazarv/react-server`のクライアントサイドナビゲーションでMantineの`NavLink`などのナビゲーションコンポーネントを使用するには、`component`プロップを介してフレームワークの`Link`コンポーネントを渡します: + +```tsx filename="src/components/MainNavigation.tsx" +"use client"; + +import { NavLink } from "@mantine/core"; +import { Link, usePathname } from "@lazarv/react-server/navigation"; + +export function MainNavigation() { + const pathname = usePathname(); + + return ( + <> + + + + ); +} +``` + + +## 拡張パッケージ + + +Mantineは追加機能のための多くの拡張パッケージを提供しています。使用するページやレイアウトでスタイルをインポートします: + +```tsx filename="src/pages/dates.tsx" +import "@mantine/dates/styles.css"; + +import { MyDates } from "../components/MyDates"; + +export default function DatesPage() { + return ; +} +``` + +よく使われるMantine拡張パッケージとそのスタイルインポート: + +| パッケージ | スタイルインポート | +|---------|-------------| +| `@mantine/dates` | `@mantine/dates/styles.css` | +| `@mantine/charts` | `@mantine/charts/styles.css` | +| `@mantine/notifications` | `@mantine/notifications/styles.css` | +| `@mantine/code-highlight` | `@mantine/code-highlight/styles.css` | +| `@mantine/carousel` | `@mantine/carousel/styles.css` | +| `@mantine/spotlight` | `@mantine/spotlight/styles.css` | +| `@mantine/nprogress` | `@mantine/nprogress/styles.css` | +| `@mantine/tiptap` | `@mantine/tiptap/styles.css` | +| `@mantine/dropzone` | `@mantine/dropzone/styles.css` | + + +## クライアント専用コンポーネント + + +一部のコンポーネント(ブラウザAPIに依存するチャートなど)はサーバーサイドレンダリングができません。`@lazarv/react-server/client`の`ClientOnly`を使用して、クライアントでのみレンダリングします: + +```tsx filename="src/pages/charts.tsx" +import "@mantine/charts/styles.css"; + +import { ClientOnly } from "@lazarv/react-server/client"; +import { MyAreaChart } from "../components/MyAreaChart"; + +export default function ChartsPage() { + return ( + + + + ); +} +``` + +> `@lazarv/react-server`でMantine UIを使用する完全な例については、[Mantine example](https://github.com/lazarv/react-server/tree/main/examples/mantine)を確認してください。 diff --git a/docs/src/pages/ja/(pages)/integrations/mui.mdx b/docs/src/pages/ja/(pages)/integrations/mui.mdx new file mode 100644 index 00000000..7c6f2972 --- /dev/null +++ b/docs/src/pages/ja/(pages)/integrations/mui.mdx @@ -0,0 +1,174 @@ +--- +title: Material UI +category: Integrations +order: 6 +--- + +import Link from "../../../../components/Link.jsx"; + +# Material UI + +`@lazarv/react-server`は、GoogleのMaterial Designを実装する人気のReactコンポーネントライブラリである[Material UI (MUI)](https://mui.com/material-ui/)と互換性があります。MUIはCSS-in-JSスタイリングにEmotionを使用しており、`@lazarv/react-server`でそのまま動作します。 + + +## インストール + + +Material UIと必要な依存関係をインストールします: + +```sh +pnpm add @mui/material @emotion/react @emotion/styled +``` + +オプションで、RobotoフォントとMaterial Iconsをインストールします: + +```sh +pnpm add @fontsource/roboto @mui/icons-material +``` + + +## テーマのセットアップ + + +Material UIテーマを設定するテーマファイルを作成します: + +```js filename="app/theme.mjs" +import { createTheme } from "@mui/material/styles"; + +const theme = createTheme({ + palette: { + mode: "light", + }, + typography: { + fontFamily: "Roboto", + }, +}); + +export default theme; +``` + + +## プロバイダーのセットアップ + + +Material UIには、適切なテーマとベースラインスタイルのために`ThemeProvider`と`CssBaseline`が必要です。アプリをラップするクライアントコンポーネントを作成します: + +```jsx filename="app/components/Layout.jsx" +"use client"; + +import Container from "@mui/material/Container"; +import CssBaseline from "@mui/material/CssBaseline"; +import { ThemeProvider } from "@mui/material/styles"; + +import theme from "../theme"; + +export default function Layout({ children }) { + return ( + + + + {children} + + + ); +} +``` + +次に、フォントをインポートし、`Layout`プロバイダーコンポーネントを使用するルートレイアウトを設定します: + +```jsx filename="app/layout.jsx" +import "@fontsource/roboto/300.css"; +import "@fontsource/roboto/400.css"; +import "@fontsource/roboto/500.css"; +import "@fontsource/roboto/700.css"; + +import Layout from "./components/Layout"; + +export default function RootLayout({ children }) { + return ( + + + + + My App + + + {children} + + + ); +} +``` + + +## ナビゲーション + + +MUIのリンクやボタンコンポーネントを`@lazarv/react-server`のクライアントサイドナビゲーションで使用するには、`component`プロップを介してフレームワークの`Link`コンポーネントを渡します: + +```jsx filename="app/page.jsx" +"use client"; + +import { Link as ReactServerLink } from "@lazarv/react-server/navigation"; +import Link from "@mui/material/Link"; + +export default function Home() { + return ( + + Go to the about page + + ); +} +``` + +同じパターンでMUIの`Button`をナビゲーションに使用できます: + +```jsx filename="app/about/page.jsx" +"use client"; + +import { Link as ReactServerLink } from "@lazarv/react-server/navigation"; +import Button from "@mui/material/Button"; + +export default function About() { + return ( + + ); +} +``` + + +## サーバーコンポーネント + + +ブラウザAPIやReactフックに依存しない一部のMUIコンポーネントは、サーバーコンポーネントで直接使用できます。`Typography`や`Link`(ナビゲーションなし)などのコンポーネントはサーバーコンポーネントで動作します: + +```jsx filename="app/components/Copyright.jsx" +import Typography from "@mui/material/Typography"; +import Link from "@mui/material/Link"; + +export default function Copyright() { + return ( + + {"Copyright © "} + + Your Website + {" "} + {new Date().getFullYear()}. + + ); +} +``` + + +## アイコン + + +`@mui/icons-material`を使用する場合、適切なモジュール解決のためにESMパスからアイコンをインポートします: + +```jsx +import LightbulbOutlined from "@mui/icons-material/esm/LightbulbOutlined"; +``` + +> `@lazarv/react-server`でMUIを使用する完全な例については、[Material UI example](https://github.com/lazarv/react-server/tree/main/examples/mui)を確認してください。 diff --git a/docs/src/pages/ja/(pages)/integrations/react-query.mdx b/docs/src/pages/ja/(pages)/integrations/react-query.mdx new file mode 100644 index 00000000..7ac3ae6a --- /dev/null +++ b/docs/src/pages/ja/(pages)/integrations/react-query.mdx @@ -0,0 +1,253 @@ +--- +title: TanStack Query +category: Integrations +order: 4 +--- + +import Link from "../../../../components/Link.jsx"; + +# TanStack Query + +`@lazarv/react-server`は、[TanStack Query](https://tanstack.com/query)(旧React Query)と連携して、サーバーサイドプリフェッチとクライアントサイドハイドレーションによる強力なデータフェッチを提供します。サーバーコンポーネントでクエリをプリフェッチし、クライアントでシームレスにデータをハイドレートすることで、不要な再フェッチを回避できます。 + + +## インストール + + +TanStack Queryをプロジェクトにインストールします: + +```sh +pnpm add @tanstack/react-query +``` + + +## セットアップ + + +TanStack Queryを使用するには、`QueryClient`を作成し、アプリを`QueryClientProvider`でラップする必要があります。`QueryClientProvider`はReactコンテキストに依存するため、クライアントコンポーネントである必要があります。 + +サーバーとブラウザの両方の環境を処理するクエリクライアントファクトリを作成します: + +```jsx filename="app/get-query-client.jsx" +import { + defaultShouldDehydrateQuery, + isServer, + QueryClient, +} from "@tanstack/react-query"; + +function makeQueryClient() { + return new QueryClient({ + defaultOptions: { + queries: { + // SSRでは、クライアントでの即座の再フェッチを避けるために + // デフォルトのstaleTimeを0より大きく設定します + staleTime: 60 * 1000, + }, + dehydrate: { + // 保留中のクエリをデハイドレーションに含める + shouldDehydrateQuery: (query) => + defaultShouldDehydrateQuery(query) || + query.state.status === "pending", + }, + }, + }); +} + +let browserQueryClient = undefined; + +export function getQueryClient() { + if (isServer) { + // サーバー: 常に新しいクエリクライアントを作成 + return makeQueryClient(); + } else { + // ブラウザ: まだない場合は新しいクエリクライアントを作成 + if (!browserQueryClient) browserQueryClient = makeQueryClient(); + return browserQueryClient; + } +} +``` + +アプリの残りの部分に`QueryClient`を提供するクライアントコンポーネントを作成します: + +```jsx filename="app/providers.jsx" +"use client"; + +import { + isServer, + QueryClient, + QueryClientProvider, +} from "@tanstack/react-query"; + +function makeQueryClient() { + return new QueryClient({ + defaultOptions: { + queries: { + staleTime: 60 * 1000, + }, + }, + }); +} + +let browserQueryClient = undefined; + +function getQueryClient() { + if (isServer) { + return makeQueryClient(); + } else { + if (!browserQueryClient) browserQueryClient = makeQueryClient(); + return browserQueryClient; + } +} + +export default function Providers({ children }) { + const queryClient = getQueryClient(); + + return ( + + {children} + + ); +} +``` + +次に、ルートレイアウトで`Providers`コンポーネントでアプリをラップします: + +```jsx filename="app/layout.jsx" +import Providers from "./providers"; + +export default function RootLayout({ children }) { + return ( + + + + {children} + + + ); +} +``` + + +## サーバーサイドプリフェッチ + + +`@lazarv/react-server`でTanStack Queryを使用する主な利点は、サーバーコンポーネントでデータをプリフェッチし、クライアントでハイドレートできることです。これにより、ローディング状態なしでデータが即座に利用可能になります。 + +サーバーコンポーネントで、クエリクライアントを使用してデータをプリフェッチし、クライアントコンポーネントを`HydrationBoundary`でラップします: + +```jsx filename="app/page.jsx" +import { dehydrate, HydrationBoundary } from "@tanstack/react-query"; + +import { getPosts } from "./get-posts"; +import { getQueryClient } from "./get-query-client"; +import Posts from "./posts"; + +export default function PostsPage() { + const queryClient = getQueryClient(); + + queryClient.prefetchQuery({ + queryKey: ["posts"], + queryFn: getPosts, + }); + + return ( + + + + ); +} +``` + + +## クライアントコンポーネント + + +クライアントコンポーネントでは、同じクエリキーで`useSuspenseQuery`(または`useQuery`)を使用できます。サーバーでデータがプリフェッチされている場合、ローディング状態なしで即座に利用可能になります: + +```jsx filename="app/posts.jsx" +"use client"; + +import { useSuspenseQuery } from "@tanstack/react-query"; +import { getPosts } from "./get-posts"; + +export default function Posts() { + const { data } = useSuspenseQuery({ + queryKey: ["posts"], + queryFn: getPosts, + }); + + return ( +
    + {data.map((post) => ( +
  • {post.title}
  • + ))} +
+ ); +} +``` + + +## 同型データフェッチ + + +サーバーとクライアント間でデータフェッチロジックを共有するために、ランタイム環境を検出する同型データフェッチャーを作成できます: + +```js filename="app/get-posts.mjs" +export async function getPosts() { + if (typeof document === "undefined") { + // サーバー: データを直接インポート + const { default: posts } = await import("../data/posts.json"); + return posts; + } else { + // クライアント: APIルートからフェッチ + const res = await fetch("/api/posts"); + return res.json(); + } +} +``` + +ファイルシステムルーティングを使用したAPIルートと組み合わせることができます: + +```jsx filename="app/api/GET.posts.jsx" +import posts from "../../data/posts.json"; + +export default async function GET() { + return new Response(JSON.stringify(posts), { + status: 200, + headers: { + "Content-Type": "application/json", + }, + }); +} +``` + + +## ネストされたハイドレーション境界 + + +異なるデータセットをプリフェッチするために、別々のサーバーコンポーネントに`HydrationBoundary`コンポーネントをネストできます。これは複数のデータ依存関係を構成するのに便利です: + +```jsx filename="app/comments-server.jsx" +import { dehydrate, HydrationBoundary } from "@tanstack/react-query"; + +import Comments from "./comments"; +import { getComments } from "./get-comments"; +import { getQueryClient } from "./get-query-client"; + +export default function CommentsServerComponent() { + const queryClient = getQueryClient(); + + queryClient.prefetchQuery({ + queryKey: ["comments"], + queryFn: getComments, + }); + + return ( + + + + ); +} +``` + +> `@lazarv/react-server`でTanStack Queryを使用する完全な例については、[TanStack Query example](https://github.com/lazarv/react-server/tree/main/examples/react-query)を確認してください。 From cb760d9957c7b3fb4ee9932984f3e41aedc5ba19 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Viktor=20L=C3=A1z=C3=A1r?= Date: Sat, 28 Feb 2026 12:53:58 +0100 Subject: [PATCH 2/2] fix: ci test results --- .github/workflows/ci.yml | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index dba28245..24581784 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -551,7 +551,18 @@ jobs: - test-build-start-apps-edge - test-build-start-apps-edge-entry - test-rsc - if: always() + if: >- + always() && !( + needs.test-dev-base.result == 'skipped' && + needs.test-build-start-base.result == 'skipped' && + needs.test-build-start-base-edge.result == 'skipped' && + needs.test-build-start-base-edge-entry.result == 'skipped' && + needs.test-dev-apps.result == 'skipped' && + needs.test-build-start-apps.result == 'skipped' && + needs.test-build-start-apps-edge.result == 'skipped' && + needs.test-build-start-apps-edge-entry.result == 'skipped' && + needs.test-rsc.result == 'skipped' + ) runs-on: ubuntu-latest steps: - name: Checkout