From 59737d6d1d8d0974b1cf76f890a7ef26ffedc10b Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 19 Oct 2026 18:16:35 +0000 Subject: [PATCH 1/5] docs: Show async boundaries with a live demo The Render Data page's Async Boundaries section opens a post in a small blog app: the post loads quickly and its comments slowly, under one AsyncBoundary, so readers see where its loading fallback goes and can move it in the code. Co-Authored-By: Claude Opus 5.5 (1M context) Claude-Session: https://claude.ai/code/session_018FerSYMdRyzxaKQdv64mGf --- .../references/data-dependency.md | 131 ++++++++++ .../references/data-dependency.md | 131 ++++++++++ .../references/data-dependency.vue.md | 132 ++++++++++ .../references/data-dependency.md | 132 ++++++++++ docs/core/getting-started/data-dependency.md | 247 ++++++++++++++++++ .../core/getting-started/data-dependency.md | 247 ++++++++++++++++++ .../core/getting-started/data-dependency.md | 247 ++++++++++++++++++ .../core/getting-started/data-dependency.md | 247 ++++++++++++++++++ website/src/fixtures/post-comments.ts | 61 +++++ website/translate/lock/es.json | 2 +- website/translate/lock/pt-BR.json | 2 +- website/translate/lock/zh-Hans.json | 2 +- 12 files changed, 1578 insertions(+), 3 deletions(-) diff --git a/.agents/skills/data-client-react/references/data-dependency.md b/.agents/skills/data-client-react/references/data-dependency.md index 8e395927b44a..df9e3fb3ef64 100644 --- a/.agents/skills/data-client-react/references/data-dependency.md +++ b/.agents/skills/data-client-react/references/data-dependency.md @@ -151,6 +151,137 @@ us to make error/loading disjoint from data usage. Instead we place [\](./AsyncBoundary.md) to handling loading and error conditions at or above navigational boundaries like **pages, routes, or [modals](https://www.appcues.com/blog/modal-dialog-windows)**. +Files with a `route` are pages mounted at that path, with its params as props; `Link` and `useNavigate()` stand for your router (with react-router, read params with `useParams()`). + +```ts title="Resources" +import { Entity, resource } from '@data-client/rest'; + +export class Post extends Entity { + id = 0; + title = ''; + body = ''; + author = ''; + avatar = ''; + static key = 'Post'; +} +export const PostResource = resource({ + path: '/posts/:id', + schema: Post, +}); + +export class Comment extends Entity { + id = 0; + postId = 0; + author = ''; + avatar = ''; + text = ''; + static key = 'Comment'; +} +export const CommentResource = resource({ + path: '/comments/:id', + searchParams: {} as { postId: string }, + schema: Comment, +}); +``` + +```tsx title="Layout" layout +export default function Layout({ children }: { children: React.ReactNode }) { + return ( + <> +
+

Field Notes

+
+ {children} + + ); +} +``` + +```tsx title="PostList" route="/" +import { useSuspense } from '@data-client/react'; +import { PostResource } from './Resources'; + +export default function PostList() { + const posts = useSuspense(PostResource.getList); + return ( +
+ {posts.map(post => ( +
+ +
+

+ {post.title} +

+ {post.author} +
+
+ ))} +
+ ); +} +``` + +```tsx title="PostPage" route="/posts/:id" {7,10} +import { AsyncBoundary } from '@data-client/react'; +import CommentList from './CommentList'; +import PostDetail from './PostDetail'; + +export default function PostPage({ id }: { id: string }) { + return ( + }> + + + + ); +} +``` + +```tsx title="PostDetail" +import { useSuspense } from '@data-client/react'; +import { PostResource } from './Resources'; + +export default function PostDetail({ id }: { id: string }) { + const post = useSuspense(PostResource.get, { id }); + return ( +
+

{post.title}

+

+ + {post.author} +

+

{post.body}

+
+ ); +} +``` + +```tsx title="CommentList" +import { useSuspense } from '@data-client/react'; +import { CommentResource } from './Resources'; + +export default function CommentList({ postId }: { postId: string }) { + const comments = useSuspense(CommentResource.getList, { postId }); + return ( +
+

Comments

+
+ {comments.map(comment => ( +
+ +
+ {comment.author} +

{comment.text}

+
+
+ ))} +
+
+ ); +} +``` + +Show the code and move the `` to wrap just ``: the post shows before its comments do. + **React Router** ```tsx {9,11} title="Dashboard.tsx" diff --git a/.agents/skills/data-client-rest/references/data-dependency.md b/.agents/skills/data-client-rest/references/data-dependency.md index c765810db0cc..082582a37039 100644 --- a/.agents/skills/data-client-rest/references/data-dependency.md +++ b/.agents/skills/data-client-rest/references/data-dependency.md @@ -151,6 +151,137 @@ us to make error/loading disjoint from data usage. Instead we place [\](https://dataclient.io/docs/api/AsyncBoundary) to handling loading and error conditions at or above navigational boundaries like **pages, routes, or [modals](https://www.appcues.com/blog/modal-dialog-windows)**. +Files with a `route` are pages mounted at that path, with its params as props; `Link` and `useNavigate()` stand for your router (with react-router, read params with `useParams()`). + +```ts title="Resources" +import { Entity, resource } from '@data-client/rest'; + +export class Post extends Entity { + id = 0; + title = ''; + body = ''; + author = ''; + avatar = ''; + static key = 'Post'; +} +export const PostResource = resource({ + path: '/posts/:id', + schema: Post, +}); + +export class Comment extends Entity { + id = 0; + postId = 0; + author = ''; + avatar = ''; + text = ''; + static key = 'Comment'; +} +export const CommentResource = resource({ + path: '/comments/:id', + searchParams: {} as { postId: string }, + schema: Comment, +}); +``` + +```tsx title="Layout" layout +export default function Layout({ children }: { children: React.ReactNode }) { + return ( + <> +
+

Field Notes

+
+ {children} + + ); +} +``` + +```tsx title="PostList" route="/" +import { useSuspense } from '@data-client/react'; +import { PostResource } from './Resources'; + +export default function PostList() { + const posts = useSuspense(PostResource.getList); + return ( +
+ {posts.map(post => ( +
+ +
+

+ {post.title} +

+ {post.author} +
+
+ ))} +
+ ); +} +``` + +```tsx title="PostPage" route="/posts/:id" {7,10} +import { AsyncBoundary } from '@data-client/react'; +import CommentList from './CommentList'; +import PostDetail from './PostDetail'; + +export default function PostPage({ id }: { id: string }) { + return ( + }> + + + + ); +} +``` + +```tsx title="PostDetail" +import { useSuspense } from '@data-client/react'; +import { PostResource } from './Resources'; + +export default function PostDetail({ id }: { id: string }) { + const post = useSuspense(PostResource.get, { id }); + return ( +
+

{post.title}

+

+ + {post.author} +

+

{post.body}

+
+ ); +} +``` + +```tsx title="CommentList" +import { useSuspense } from '@data-client/react'; +import { CommentResource } from './Resources'; + +export default function CommentList({ postId }: { postId: string }) { + const comments = useSuspense(CommentResource.getList, { postId }); + return ( +
+

Comments

+
+ {comments.map(comment => ( +
+ +
+ {comment.author} +

{comment.text}

+
+
+ ))} +
+
+ ); +} +``` + +Show the code and move the `` to wrap just ``: the post shows before its comments do. + **React Router** ```tsx {9,11} title="Dashboard.tsx" diff --git a/.agents/skills/data-client-rest/references/data-dependency.vue.md b/.agents/skills/data-client-rest/references/data-dependency.vue.md index b5eb62a70cae..6185e10a8648 100644 --- a/.agents/skills/data-client-rest/references/data-dependency.vue.md +++ b/.agents/skills/data-client-rest/references/data-dependency.vue.md @@ -160,6 +160,138 @@ us to make error/loading disjoint from data usage. Instead we place Vue's built-in [\](https://vuejs.org/guide/built-ins/suspense.html) along with [onErrorCaptured()](https://vuejs.org/api/composition-api-lifecycle.html#onerrorcaptured) to handling loading and error conditions at or above navigational boundaries like **pages, routes, or [modals](https://www.appcues.com/blog/modal-dialog-windows)**. +Files with a `route` are pages mounted at that path, with its params as props (vue-router `props: true`). + +```ts title="Resources" +import { Entity, resource } from '@data-client/rest'; + +export class Post extends Entity { + id = 0; + title = ''; + body = ''; + author = ''; + avatar = ''; + static key = 'Post'; +} +export const PostResource = resource({ + path: '/posts/:id', + schema: Post, +}); + +export class Comment extends Entity { + id = 0; + postId = 0; + author = ''; + avatar = ''; + text = ''; + static key = 'Comment'; +} +export const CommentResource = resource({ + path: '/comments/:id', + searchParams: {} as { postId: string }, + schema: Comment, +}); +``` + +```html title="Layout.vue" layout + +``` + +```html title="PostList.vue" route="/" + + + +``` + +```html title="PostPage.vue" route="/posts/:id" {9,15} + + + +``` + +```html title="PostDetail.vue" + + + +``` + +```html title="CommentList.vue" + + + +``` + ```html title="Dashboard.vue" {15-16,23} + + +``` + +```html title="PostPage.vue" route="/posts/:id" {9,15} + + + +``` + +```html title="PostDetail.vue" + + + +``` + +```html title="CommentList.vue" + + + +``` + ```html title="Dashboard.vue" {15-16,23} + + +``` + +```html title="PostPage.vue" route="/posts/:id" {9,15} + + + +``` + +```html title="PostDetail.vue" collapsed + + + +``` + +```html title="CommentList.vue" collapsed + + + +``` + +::: + + + +:react[Show the code and move the `` to wrap just ``: the post shows before its comments do.] + :::react diff --git a/website/i18n/es/docs/core/getting-started/data-dependency.md b/website/i18n/es/docs/core/getting-started/data-dependency.md index dd88809573d9..3069dc521d77 100644 --- a/website/i18n/es/docs/core/getting-started/data-dependency.md +++ b/website/i18n/es/docs/core/getting-started/data-dependency.md @@ -9,6 +9,7 @@ sidebar_label: Renderizar datos import DataColocation from '@site/src/components/DataColocation'; +import { boundaryFixtures } from '@site/src/fixtures/post-comments'; import LanguageTabs from '@site/src/components/LanguageTabs'; import FrameworkPlayground from '@site/src/components/FrameworkPlayground'; import ConditionalDependencies from '../shared/\_conditional_dependencies.mdx'; @@ -17,6 +18,12 @@ import { detailFixtures, listFixtures } from '@site/src/fixtures/profiles'; import UseLive from '../shared/\_useLive.mdx'; import AsyncBoundaryExamples from '../shared/\_AsyncBoundary.mdx'; +export const boundaryDemo = [ + { delay: 1000, click: 'a[href="/posts/1"]' }, + // past the comments' 2s delay, so the page shows in full + { wait: 3000 }, +]; + # Renderizar datos asíncronos Haz que tus componentes sean reutilizables vinculando los datos donde los **usas** con el [useSuspense()](../api/useSuspense.md) de una sola línea, @@ -268,6 +275,246 @@ separar el manejo de errores y de carga del uso de los datos. En su lugar, colocamos :react[[<AsyncBoundary /\>](../api/AsyncBoundary.md)]:vue[el [<Suspense /\>](https://vuejs.org/guide/built-ins/suspense.html) integrado de Vue junto con [onErrorCaptured()](https://vuejs.org/api/composition-api-lifecycle.html#onerrorcaptured)] para manejar las condiciones de carga y error en o por encima de los límites de navegación, como **páginas, rutas o [modales](https://www.appcues.com/blog/modal-dialog-windows)**. + + +```ts title="Resources" collapsed +import { Entity, resource } from '@data-client/rest'; + +export class Post extends Entity { + id = 0; + title = ''; + body = ''; + author = ''; + avatar = ''; + static key = 'Post'; +} +export const PostResource = resource({ + path: '/posts/:id', + schema: Post, +}); + +export class Comment extends Entity { + id = 0; + postId = 0; + author = ''; + avatar = ''; + text = ''; + static key = 'Comment'; +} +export const CommentResource = resource({ + path: '/comments/:id', + searchParams: {} as { postId: string }, + schema: Comment, +}); +``` + +:::react + +```tsx title="Layout" layout collapsed +export default function Layout({ children }: { children: React.ReactNode }) { + return ( + <> +
+

Field Notes

+
+ {children} + + ); +} +``` + +```tsx title="PostList" route="/" collapsed +import { useSuspense } from '@data-client/react'; +import { PostResource } from './Resources'; + +export default function PostList() { + const posts = useSuspense(PostResource.getList); + return ( +
+ {posts.map(post => ( +
+ +
+

+ {post.title} +

+ {post.author} +
+
+ ))} +
+ ); +} +``` + +```tsx title="PostPage" route="/posts/:id" {7,10} +import { AsyncBoundary } from '@data-client/react'; +import CommentList from './CommentList'; +import PostDetail from './PostDetail'; + +export default function PostPage({ id }: { id: string }) { + return ( + }> + + + + ); +} +``` + +```tsx title="PostDetail" collapsed +import { useSuspense } from '@data-client/react'; +import { PostResource } from './Resources'; + +export default function PostDetail({ id }: { id: string }) { + const post = useSuspense(PostResource.get, { id }); + return ( +
+

{post.title}

+

+ + {post.author} +

+

{post.body}

+
+ ); +} +``` + +```tsx title="CommentList" collapsed +import { useSuspense } from '@data-client/react'; +import { CommentResource } from './Resources'; + +export default function CommentList({ postId }: { postId: string }) { + const comments = useSuspense(CommentResource.getList, { postId }); + return ( +
+

Comments

+
+ {comments.map(comment => ( +
+ +
+ {comment.author} +

{comment.text}

+
+
+ ))} +
+
+ ); +} +``` + +::: + +:::vue + +```html title="Layout.vue" layout collapsed + +``` + +```html title="PostList.vue" route="/" collapsed + + + +``` + +```html title="PostPage.vue" route="/posts/:id" {9,15} + + + +``` + +```html title="PostDetail.vue" collapsed + + + +``` + +```html title="CommentList.vue" collapsed + + + +``` + +::: + +
+ +:react[Muestra el código y mueve el `` para que envuelva solo ``: la publicación se muestra antes que sus comentarios.] + :::react diff --git a/website/i18n/pt-BR/docs/core/getting-started/data-dependency.md b/website/i18n/pt-BR/docs/core/getting-started/data-dependency.md index e86a697314bf..de3016d218e0 100644 --- a/website/i18n/pt-BR/docs/core/getting-started/data-dependency.md +++ b/website/i18n/pt-BR/docs/core/getting-started/data-dependency.md @@ -9,6 +9,7 @@ sidebar_label: Renderizar dados import DataColocation from '@site/src/components/DataColocation'; +import { boundaryFixtures } from '@site/src/fixtures/post-comments'; import LanguageTabs from '@site/src/components/LanguageTabs'; import FrameworkPlayground from '@site/src/components/FrameworkPlayground'; import ConditionalDependencies from '../shared/\_conditional_dependencies.mdx'; @@ -17,6 +18,12 @@ import { detailFixtures, listFixtures } from '@site/src/fixtures/profiles'; import UseLive from '../shared/\_useLive.mdx'; import AsyncBoundaryExamples from '../shared/\_AsyncBoundary.mdx'; +export const boundaryDemo = [ + { delay: 1000, click: 'a[href="/posts/1"]' }, + // past the comments' 2s delay, so the page shows in full + { wait: 3000 }, +]; + # Renderizando dados assíncronos Torne seus componentes reutilizáveis associando os dados onde você os **usa**, com o [useSuspense()](../api/useSuspense.md) de uma linha, @@ -268,6 +275,246 @@ separar o tratamento de erro/carregamento do uso dos dados. Em vez disso, colocamos :react[[<AsyncBoundary /\>](../api/AsyncBoundary.md)]:vue[o [<Suspense /\>](https://vuejs.org/guide/built-ins/suspense.html) nativo do Vue junto com [onErrorCaptured()](https://vuejs.org/api/composition-api-lifecycle.html#onerrorcaptured)] para tratar as condições de carregamento e de erro em limites de navegação, ou acima deles, como **páginas, rotas ou [modais](https://www.appcues.com/blog/modal-dialog-windows)**. + + +```ts title="Resources" collapsed +import { Entity, resource } from '@data-client/rest'; + +export class Post extends Entity { + id = 0; + title = ''; + body = ''; + author = ''; + avatar = ''; + static key = 'Post'; +} +export const PostResource = resource({ + path: '/posts/:id', + schema: Post, +}); + +export class Comment extends Entity { + id = 0; + postId = 0; + author = ''; + avatar = ''; + text = ''; + static key = 'Comment'; +} +export const CommentResource = resource({ + path: '/comments/:id', + searchParams: {} as { postId: string }, + schema: Comment, +}); +``` + +:::react + +```tsx title="Layout" layout collapsed +export default function Layout({ children }: { children: React.ReactNode }) { + return ( + <> +
+

Field Notes

+
+ {children} + + ); +} +``` + +```tsx title="PostList" route="/" collapsed +import { useSuspense } from '@data-client/react'; +import { PostResource } from './Resources'; + +export default function PostList() { + const posts = useSuspense(PostResource.getList); + return ( +
+ {posts.map(post => ( +
+ +
+

+ {post.title} +

+ {post.author} +
+
+ ))} +
+ ); +} +``` + +```tsx title="PostPage" route="/posts/:id" {7,10} +import { AsyncBoundary } from '@data-client/react'; +import CommentList from './CommentList'; +import PostDetail from './PostDetail'; + +export default function PostPage({ id }: { id: string }) { + return ( + }> + + + + ); +} +``` + +```tsx title="PostDetail" collapsed +import { useSuspense } from '@data-client/react'; +import { PostResource } from './Resources'; + +export default function PostDetail({ id }: { id: string }) { + const post = useSuspense(PostResource.get, { id }); + return ( +
+

{post.title}

+

+ + {post.author} +

+

{post.body}

+
+ ); +} +``` + +```tsx title="CommentList" collapsed +import { useSuspense } from '@data-client/react'; +import { CommentResource } from './Resources'; + +export default function CommentList({ postId }: { postId: string }) { + const comments = useSuspense(CommentResource.getList, { postId }); + return ( +
+

Comments

+
+ {comments.map(comment => ( +
+ +
+ {comment.author} +

{comment.text}

+
+
+ ))} +
+
+ ); +} +``` + +::: + +:::vue + +```html title="Layout.vue" layout collapsed + +``` + +```html title="PostList.vue" route="/" collapsed + + + +``` + +```html title="PostPage.vue" route="/posts/:id" {9,15} + + + +``` + +```html title="PostDetail.vue" collapsed + + + +``` + +```html title="CommentList.vue" collapsed + + + +``` + +::: + +
+ +:react[Mostre o código e mova o `` para envolver apenas o ``: o post aparece antes dos comentários.] + :::react diff --git a/website/i18n/zh-Hans/docs/core/getting-started/data-dependency.md b/website/i18n/zh-Hans/docs/core/getting-started/data-dependency.md index 4fd0e8db731e..439b1158ed79 100644 --- a/website/i18n/zh-Hans/docs/core/getting-started/data-dependency.md +++ b/website/i18n/zh-Hans/docs/core/getting-started/data-dependency.md @@ -9,6 +9,7 @@ sidebar_label: 渲染数据 import DataColocation from '@site/src/components/DataColocation'; +import { boundaryFixtures } from '@site/src/fixtures/post-comments'; import LanguageTabs from '@site/src/components/LanguageTabs'; import FrameworkPlayground from '@site/src/components/FrameworkPlayground'; import ConditionalDependencies from '../shared/\_conditional_dependencies.mdx'; @@ -17,6 +18,12 @@ import { detailFixtures, listFixtures } from '@site/src/fixtures/profiles'; import UseLive from '../shared/\_useLive.mdx'; import AsyncBoundaryExamples from '../shared/\_AsyncBoundary.mdx'; +export const boundaryDemo = [ + { delay: 1000, click: 'a[href="/posts/1"]' }, + // past the comments' 2s delay, so the page shows in full + { wait: 3000 }, +]; + # 渲染异步数据 只需一行 [useSuspense()](../api/useSuspense.md),就能在**使用**数据的地方绑定数据,让你的组件可复用; @@ -268,6 +275,246 @@ Reactive Data Client 会在[数据变化](./mutations.md)时立即自动更新 我们会在**页面、路由或[模态框](https://www.appcues.com/blog/modal-dialog-windows)**等导航边界处或其上层放置 :react[[<AsyncBoundary /\>](../api/AsyncBoundary.md)]:vue[Vue 内置的 [<Suspense /\>](https://vuejs.org/guide/built-ins/suspense.html) 以及 [onErrorCaptured()](https://vuejs.org/api/composition-api-lifecycle.html#onerrorcaptured)], 用来处理加载和错误状态。 + + +```ts title="Resources" collapsed +import { Entity, resource } from '@data-client/rest'; + +export class Post extends Entity { + id = 0; + title = ''; + body = ''; + author = ''; + avatar = ''; + static key = 'Post'; +} +export const PostResource = resource({ + path: '/posts/:id', + schema: Post, +}); + +export class Comment extends Entity { + id = 0; + postId = 0; + author = ''; + avatar = ''; + text = ''; + static key = 'Comment'; +} +export const CommentResource = resource({ + path: '/comments/:id', + searchParams: {} as { postId: string }, + schema: Comment, +}); +``` + +:::react + +```tsx title="Layout" layout collapsed +export default function Layout({ children }: { children: React.ReactNode }) { + return ( + <> +
+

Field Notes

+
+ {children} + + ); +} +``` + +```tsx title="PostList" route="/" collapsed +import { useSuspense } from '@data-client/react'; +import { PostResource } from './Resources'; + +export default function PostList() { + const posts = useSuspense(PostResource.getList); + return ( +
+ {posts.map(post => ( +
+ +
+

+ {post.title} +

+ {post.author} +
+
+ ))} +
+ ); +} +``` + +```tsx title="PostPage" route="/posts/:id" {7,10} +import { AsyncBoundary } from '@data-client/react'; +import CommentList from './CommentList'; +import PostDetail from './PostDetail'; + +export default function PostPage({ id }: { id: string }) { + return ( + }> + + + + ); +} +``` + +```tsx title="PostDetail" collapsed +import { useSuspense } from '@data-client/react'; +import { PostResource } from './Resources'; + +export default function PostDetail({ id }: { id: string }) { + const post = useSuspense(PostResource.get, { id }); + return ( +
+

{post.title}

+

+ + {post.author} +

+

{post.body}

+
+ ); +} +``` + +```tsx title="CommentList" collapsed +import { useSuspense } from '@data-client/react'; +import { CommentResource } from './Resources'; + +export default function CommentList({ postId }: { postId: string }) { + const comments = useSuspense(CommentResource.getList, { postId }); + return ( +
+

Comments

+
+ {comments.map(comment => ( +
+ +
+ {comment.author} +

{comment.text}

+
+
+ ))} +
+
+ ); +} +``` + +::: + +:::vue + +```html title="Layout.vue" layout collapsed + +``` + +```html title="PostList.vue" route="/" collapsed + + + +``` + +```html title="PostPage.vue" route="/posts/:id" {9,15} + + + +``` + +```html title="PostDetail.vue" collapsed + + + +``` + +```html title="CommentList.vue" collapsed + + + +``` + +::: + +
+ +:react[打开代码,把 `` 移到只包裹 ``:帖子就会先于评论显示。] + :::react diff --git a/website/src/fixtures/post-comments.ts b/website/src/fixtures/post-comments.ts index cf5d6468e92d..ba28006a2a6a 100644 --- a/website/src/fixtures/post-comments.ts +++ b/website/src/fixtures/post-comments.ts @@ -67,3 +67,64 @@ export const parallelFetchFixtures = [ delay, }, ]; + +const posts = [ + { + id: 1, + title: 'Moving to a new city', + body: 'Three weeks in, the boxes are mostly gone. What I packed first turned out to matter least, and the one thing I left behind, a beat-up desk lamp, is the thing I miss every evening. Here is what I would pack differently next time.', + author: 'Maya Chen', + avatar: 'https://i.pravatar.cc/64?img=47', + }, + { + id: 2, + title: 'Weekend bread baking', + body: 'A slow overnight rise in the fridge makes all the difference. The dough gets easier to shape, the crust browns better, and the whole loaf tastes like you tried much harder than you did.', + author: 'Sam Rivera', + avatar: 'https://i.pravatar.cc/64?img=12', + }, + { + id: 3, + title: 'A week without notifications', + body: 'I turned off every notification on my phone for seven days. The first two were strange. By the end, I was checking messages on my own schedule and getting a lot more done.', + author: 'Priya Patel', + avatar: 'https://i.pravatar.cc/64?img=32', + }, +]; + +/** The post arrives quickly and its comments slowly, so where the + * AsyncBoundary sits decides what shows while they load */ +export const boundaryFixtures = [ + { + endpoint: PostResource.getList, + args: [], + response: posts, + }, + { + endpoint: PostResource.get, + response: ({ id }: { id: string }) => + posts.find(post => post.id === Number(id)), + delay: 500, + }, + { + endpoint: CommentResource.getList, + // every post shares these comments + response: () => [ + { + id: 1, + postId: 1, + author: 'Alex Kim', + avatar: 'https://i.pravatar.cc/64?img=8', + text: 'Loved this, thanks for writing it up.', + }, + { + id: 2, + postId: 1, + author: 'Jordan Lee', + avatar: 'https://i.pravatar.cc/64?img=26', + text: 'Saving this for later.', + }, + ], + delay: 2000, + }, +]; diff --git a/website/translate/lock/es.json b/website/translate/lock/es.json index a372cea562b4..91c4abb34118 100644 --- a/website/translate/lock/es.json +++ b/website/translate/lock/es.json @@ -43,7 +43,7 @@ "docs/core/concepts/reactivity.md": "1f3f4c5c445db48d04de10966da56a9946d246f8", "docs/core/concepts/validation.md": "5119ecd2fac7be8250dbc79c8980e082641a40a6", "docs/core/getting-started/agent-skills.md": "f856007e07c8dd63ec585286b77ff19433a0116c", - "docs/core/getting-started/data-dependency.md": "2e19945fc76917e7030449636cd720d89af6cac9", + "docs/core/getting-started/data-dependency.md": "bbadd6102587f72900c4098b0493866c786feb48", "docs/core/getting-started/debugging.md": "d2b5c1e8e5faec00320384a359facc15532129d3", "docs/core/getting-started/installation.md": "c93346e05d241b4f2967ed1ea4ef600286320025", "docs/core/getting-started/mutations.md": "2dbc68d4c23d8fdf068f423f05a30b954d11a17b", diff --git a/website/translate/lock/pt-BR.json b/website/translate/lock/pt-BR.json index 0fd0e80b0d2c..7cb43767bb86 100644 --- a/website/translate/lock/pt-BR.json +++ b/website/translate/lock/pt-BR.json @@ -43,7 +43,7 @@ "docs/core/concepts/reactivity.md": "1f3f4c5c445db48d04de10966da56a9946d246f8", "docs/core/concepts/validation.md": "5119ecd2fac7be8250dbc79c8980e082641a40a6", "docs/core/getting-started/agent-skills.md": "f856007e07c8dd63ec585286b77ff19433a0116c", - "docs/core/getting-started/data-dependency.md": "2e19945fc76917e7030449636cd720d89af6cac9", + "docs/core/getting-started/data-dependency.md": "bbadd6102587f72900c4098b0493866c786feb48", "docs/core/getting-started/debugging.md": "d2b5c1e8e5faec00320384a359facc15532129d3", "docs/core/getting-started/installation.md": "c93346e05d241b4f2967ed1ea4ef600286320025", "docs/core/getting-started/mutations.md": "2dbc68d4c23d8fdf068f423f05a30b954d11a17b", diff --git a/website/translate/lock/zh-Hans.json b/website/translate/lock/zh-Hans.json index 46fbb4ad2ca0..5684571e74ce 100644 --- a/website/translate/lock/zh-Hans.json +++ b/website/translate/lock/zh-Hans.json @@ -43,7 +43,7 @@ "docs/core/concepts/reactivity.md": "1f3f4c5c445db48d04de10966da56a9946d246f8", "docs/core/concepts/validation.md": "5119ecd2fac7be8250dbc79c8980e082641a40a6", "docs/core/getting-started/agent-skills.md": "f856007e07c8dd63ec585286b77ff19433a0116c", - "docs/core/getting-started/data-dependency.md": "2e19945fc76917e7030449636cd720d89af6cac9", + "docs/core/getting-started/data-dependency.md": "bbadd6102587f72900c4098b0493866c786feb48", "docs/core/getting-started/debugging.md": "d2b5c1e8e5faec00320384a359facc15532129d3", "docs/core/getting-started/installation.md": "c93346e05d241b4f2967ed1ea4ef600286320025", "docs/core/getting-started/mutations.md": "2dbc68d4c23d8fdf068f423f05a30b954d11a17b", From 8085b6c66bb722f5e24422d8eb8d55c4b502f5ed Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 21 Oct 2026 16:22:23 +0000 Subject: [PATCH 2/5] docs: Give the Vue Field Notes routes a Suspense boundary PostList.vue awaits in setup, so the router's view needs a Suspense for the list to render when readers copy the Vue code. Co-Authored-By: Claude Opus 5.5 (1M context) Claude-Session: https://claude.ai/code/session_018FerSYMdRyzxaKQdv64mGf --- .../data-client-rest/references/data-dependency.vue.md | 7 ++++++- .../skills/data-client-vue/references/data-dependency.md | 7 ++++++- docs/core/getting-started/data-dependency.md | 7 ++++++- .../i18n/es/docs/core/getting-started/data-dependency.md | 7 ++++++- .../pt-BR/docs/core/getting-started/data-dependency.md | 7 ++++++- .../zh-Hans/docs/core/getting-started/data-dependency.md | 7 ++++++- website/translate/lock/es.json | 2 +- website/translate/lock/pt-BR.json | 2 +- website/translate/lock/zh-Hans.json | 2 +- 9 files changed, 39 insertions(+), 9 deletions(-) diff --git a/.agents/skills/data-client-rest/references/data-dependency.vue.md b/.agents/skills/data-client-rest/references/data-dependency.vue.md index 6185e10a8648..849b1271011d 100644 --- a/.agents/skills/data-client-rest/references/data-dependency.vue.md +++ b/.agents/skills/data-client-rest/references/data-dependency.vue.md @@ -198,7 +198,12 @@ export const CommentResource = resource({

Field Notes

- + + + + + + ``` diff --git a/.agents/skills/data-client-vue/references/data-dependency.md b/.agents/skills/data-client-vue/references/data-dependency.md index 434a0868b388..e7ae6c0a3581 100644 --- a/.agents/skills/data-client-vue/references/data-dependency.md +++ b/.agents/skills/data-client-vue/references/data-dependency.md @@ -198,7 +198,12 @@ export const CommentResource = resource({

Field Notes

- + + + + + + ``` diff --git a/docs/core/getting-started/data-dependency.md b/docs/core/getting-started/data-dependency.md index bbadd6102587..f7b70948425e 100644 --- a/docs/core/getting-started/data-dependency.md +++ b/docs/core/getting-started/data-dependency.md @@ -415,7 +415,12 @@ export default function CommentList({ postId }: { postId: string }) {

Field Notes

- + + + + + + ``` diff --git a/website/i18n/es/docs/core/getting-started/data-dependency.md b/website/i18n/es/docs/core/getting-started/data-dependency.md index 3069dc521d77..f12fc45de8a3 100644 --- a/website/i18n/es/docs/core/getting-started/data-dependency.md +++ b/website/i18n/es/docs/core/getting-started/data-dependency.md @@ -415,7 +415,12 @@ export default function CommentList({ postId }: { postId: string }) {

Field Notes

- + + + + + + ``` diff --git a/website/i18n/pt-BR/docs/core/getting-started/data-dependency.md b/website/i18n/pt-BR/docs/core/getting-started/data-dependency.md index de3016d218e0..1e3a16849687 100644 --- a/website/i18n/pt-BR/docs/core/getting-started/data-dependency.md +++ b/website/i18n/pt-BR/docs/core/getting-started/data-dependency.md @@ -415,7 +415,12 @@ export default function CommentList({ postId }: { postId: string }) {

Field Notes

- + + + + + + ``` diff --git a/website/i18n/zh-Hans/docs/core/getting-started/data-dependency.md b/website/i18n/zh-Hans/docs/core/getting-started/data-dependency.md index 439b1158ed79..191668b1f591 100644 --- a/website/i18n/zh-Hans/docs/core/getting-started/data-dependency.md +++ b/website/i18n/zh-Hans/docs/core/getting-started/data-dependency.md @@ -415,7 +415,12 @@ export default function CommentList({ postId }: { postId: string }) {

Field Notes

- + + + + + + ``` diff --git a/website/translate/lock/es.json b/website/translate/lock/es.json index 91c4abb34118..9361ba0d880b 100644 --- a/website/translate/lock/es.json +++ b/website/translate/lock/es.json @@ -43,7 +43,7 @@ "docs/core/concepts/reactivity.md": "1f3f4c5c445db48d04de10966da56a9946d246f8", "docs/core/concepts/validation.md": "5119ecd2fac7be8250dbc79c8980e082641a40a6", "docs/core/getting-started/agent-skills.md": "f856007e07c8dd63ec585286b77ff19433a0116c", - "docs/core/getting-started/data-dependency.md": "bbadd6102587f72900c4098b0493866c786feb48", + "docs/core/getting-started/data-dependency.md": "f7b70948425e2a7a10fde2cafee92e91d11a1cb9", "docs/core/getting-started/debugging.md": "d2b5c1e8e5faec00320384a359facc15532129d3", "docs/core/getting-started/installation.md": "c93346e05d241b4f2967ed1ea4ef600286320025", "docs/core/getting-started/mutations.md": "2dbc68d4c23d8fdf068f423f05a30b954d11a17b", diff --git a/website/translate/lock/pt-BR.json b/website/translate/lock/pt-BR.json index 7cb43767bb86..be06fc0615da 100644 --- a/website/translate/lock/pt-BR.json +++ b/website/translate/lock/pt-BR.json @@ -43,7 +43,7 @@ "docs/core/concepts/reactivity.md": "1f3f4c5c445db48d04de10966da56a9946d246f8", "docs/core/concepts/validation.md": "5119ecd2fac7be8250dbc79c8980e082641a40a6", "docs/core/getting-started/agent-skills.md": "f856007e07c8dd63ec585286b77ff19433a0116c", - "docs/core/getting-started/data-dependency.md": "bbadd6102587f72900c4098b0493866c786feb48", + "docs/core/getting-started/data-dependency.md": "f7b70948425e2a7a10fde2cafee92e91d11a1cb9", "docs/core/getting-started/debugging.md": "d2b5c1e8e5faec00320384a359facc15532129d3", "docs/core/getting-started/installation.md": "c93346e05d241b4f2967ed1ea4ef600286320025", "docs/core/getting-started/mutations.md": "2dbc68d4c23d8fdf068f423f05a30b954d11a17b", diff --git a/website/translate/lock/zh-Hans.json b/website/translate/lock/zh-Hans.json index 5684571e74ce..7a29d00058bd 100644 --- a/website/translate/lock/zh-Hans.json +++ b/website/translate/lock/zh-Hans.json @@ -43,7 +43,7 @@ "docs/core/concepts/reactivity.md": "1f3f4c5c445db48d04de10966da56a9946d246f8", "docs/core/concepts/validation.md": "5119ecd2fac7be8250dbc79c8980e082641a40a6", "docs/core/getting-started/agent-skills.md": "f856007e07c8dd63ec585286b77ff19433a0116c", - "docs/core/getting-started/data-dependency.md": "bbadd6102587f72900c4098b0493866c786feb48", + "docs/core/getting-started/data-dependency.md": "f7b70948425e2a7a10fde2cafee92e91d11a1cb9", "docs/core/getting-started/debugging.md": "d2b5c1e8e5faec00320384a359facc15532129d3", "docs/core/getting-started/installation.md": "c93346e05d241b4f2967ed1ea4ef600286320025", "docs/core/getting-started/mutations.md": "2dbc68d4c23d8fdf068f423f05a30b954d11a17b", From fdb2db2c4c0e458ad9ba1917c864f96b9ae2dc2f Mon Sep 17 00:00:00 2001 From: Claude Date: Sat, 24 Oct 2026 23:31:20 +0000 Subject: [PATCH 3/5] docs: The Field Notes byline sizes its avatar with Avatar's size prop Co-Authored-By: Claude Opus 5.5 (1M context) Claude-Session: https://claude.ai/code/session_018FerSYMdRyzxaKQdv64mGf --- .../data-client-react/references/data-dependency.md | 4 ++-- .../skills/data-client-rest/references/data-dependency.md | 4 ++-- .../data-client-rest/references/data-dependency.vue.md | 4 ++-- .../skills/data-client-vue/references/data-dependency.md | 4 ++-- docs/core/getting-started/data-dependency.md | 8 ++++---- .../i18n/es/docs/core/getting-started/data-dependency.md | 8 ++++---- .../pt-BR/docs/core/getting-started/data-dependency.md | 8 ++++---- .../zh-Hans/docs/core/getting-started/data-dependency.md | 8 ++++---- website/translate/lock/es.json | 2 +- website/translate/lock/pt-BR.json | 2 +- website/translate/lock/zh-Hans.json | 2 +- 11 files changed, 27 insertions(+), 27 deletions(-) diff --git a/.agents/skills/data-client-react/references/data-dependency.md b/.agents/skills/data-client-react/references/data-dependency.md index df9e3fb3ef64..9cf3b35874fe 100644 --- a/.agents/skills/data-client-react/references/data-dependency.md +++ b/.agents/skills/data-client-react/references/data-dependency.md @@ -246,8 +246,8 @@ export default function PostDetail({ id }: { id: string }) {

{post.title}

- - {post.author} + + {post.author}

{post.body}

diff --git a/.agents/skills/data-client-rest/references/data-dependency.md b/.agents/skills/data-client-rest/references/data-dependency.md index 082582a37039..67e56dc89724 100644 --- a/.agents/skills/data-client-rest/references/data-dependency.md +++ b/.agents/skills/data-client-rest/references/data-dependency.md @@ -246,8 +246,8 @@ export default function PostDetail({ id }: { id: string }) {

{post.title}

- - {post.author} + + {post.author}

{post.body}

diff --git a/.agents/skills/data-client-rest/references/data-dependency.vue.md b/.agents/skills/data-client-rest/references/data-dependency.vue.md index 849b1271011d..f3e4b62a0678 100644 --- a/.agents/skills/data-client-rest/references/data-dependency.vue.md +++ b/.agents/skills/data-client-rest/references/data-dependency.vue.md @@ -262,8 +262,8 @@ export const CommentResource = resource({

{{ post.title }}

{{ post.body }}

diff --git a/.agents/skills/data-client-vue/references/data-dependency.md b/.agents/skills/data-client-vue/references/data-dependency.md index e7ae6c0a3581..28d53dec3a7e 100644 --- a/.agents/skills/data-client-vue/references/data-dependency.md +++ b/.agents/skills/data-client-vue/references/data-dependency.md @@ -262,8 +262,8 @@ export const CommentResource = resource({

{{ post.title }}

{{ post.body }}

diff --git a/docs/core/getting-started/data-dependency.md b/docs/core/getting-started/data-dependency.md index f7b70948425e..db40ba054969 100644 --- a/docs/core/getting-started/data-dependency.md +++ b/docs/core/getting-started/data-dependency.md @@ -372,8 +372,8 @@ export default function PostDetail({ id }: { id: string }) {

{post.title}

- - {post.author} + + {post.author}

{post.body}

@@ -479,8 +479,8 @@ export default function CommentList({ postId }: { postId: string }) {

{{ post.title }}

{{ post.body }}

diff --git a/website/i18n/es/docs/core/getting-started/data-dependency.md b/website/i18n/es/docs/core/getting-started/data-dependency.md index f12fc45de8a3..9f5a0672b2a6 100644 --- a/website/i18n/es/docs/core/getting-started/data-dependency.md +++ b/website/i18n/es/docs/core/getting-started/data-dependency.md @@ -372,8 +372,8 @@ export default function PostDetail({ id }: { id: string }) {

{post.title}

- - {post.author} + + {post.author}

{post.body}

@@ -479,8 +479,8 @@ export default function CommentList({ postId }: { postId: string }) {

{{ post.title }}

{{ post.body }}

diff --git a/website/i18n/pt-BR/docs/core/getting-started/data-dependency.md b/website/i18n/pt-BR/docs/core/getting-started/data-dependency.md index 1e3a16849687..303147950d81 100644 --- a/website/i18n/pt-BR/docs/core/getting-started/data-dependency.md +++ b/website/i18n/pt-BR/docs/core/getting-started/data-dependency.md @@ -372,8 +372,8 @@ export default function PostDetail({ id }: { id: string }) {

{post.title}

- - {post.author} + + {post.author}

{post.body}

@@ -479,8 +479,8 @@ export default function CommentList({ postId }: { postId: string }) {

{{ post.title }}

{{ post.body }}

diff --git a/website/i18n/zh-Hans/docs/core/getting-started/data-dependency.md b/website/i18n/zh-Hans/docs/core/getting-started/data-dependency.md index 191668b1f591..38c93a7dc2c7 100644 --- a/website/i18n/zh-Hans/docs/core/getting-started/data-dependency.md +++ b/website/i18n/zh-Hans/docs/core/getting-started/data-dependency.md @@ -372,8 +372,8 @@ export default function PostDetail({ id }: { id: string }) {

{post.title}

- - {post.author} + + {post.author}

{post.body}

@@ -479,8 +479,8 @@ export default function CommentList({ postId }: { postId: string }) {

{{ post.title }}

{{ post.body }}

diff --git a/website/translate/lock/es.json b/website/translate/lock/es.json index 9361ba0d880b..7a694c2307c9 100644 --- a/website/translate/lock/es.json +++ b/website/translate/lock/es.json @@ -43,7 +43,7 @@ "docs/core/concepts/reactivity.md": "1f3f4c5c445db48d04de10966da56a9946d246f8", "docs/core/concepts/validation.md": "5119ecd2fac7be8250dbc79c8980e082641a40a6", "docs/core/getting-started/agent-skills.md": "f856007e07c8dd63ec585286b77ff19433a0116c", - "docs/core/getting-started/data-dependency.md": "f7b70948425e2a7a10fde2cafee92e91d11a1cb9", + "docs/core/getting-started/data-dependency.md": "db40ba054969b3ee7dc90939030594f5d664dc18", "docs/core/getting-started/debugging.md": "d2b5c1e8e5faec00320384a359facc15532129d3", "docs/core/getting-started/installation.md": "c93346e05d241b4f2967ed1ea4ef600286320025", "docs/core/getting-started/mutations.md": "2dbc68d4c23d8fdf068f423f05a30b954d11a17b", diff --git a/website/translate/lock/pt-BR.json b/website/translate/lock/pt-BR.json index be06fc0615da..3a99dd963774 100644 --- a/website/translate/lock/pt-BR.json +++ b/website/translate/lock/pt-BR.json @@ -43,7 +43,7 @@ "docs/core/concepts/reactivity.md": "1f3f4c5c445db48d04de10966da56a9946d246f8", "docs/core/concepts/validation.md": "5119ecd2fac7be8250dbc79c8980e082641a40a6", "docs/core/getting-started/agent-skills.md": "f856007e07c8dd63ec585286b77ff19433a0116c", - "docs/core/getting-started/data-dependency.md": "f7b70948425e2a7a10fde2cafee92e91d11a1cb9", + "docs/core/getting-started/data-dependency.md": "db40ba054969b3ee7dc90939030594f5d664dc18", "docs/core/getting-started/debugging.md": "d2b5c1e8e5faec00320384a359facc15532129d3", "docs/core/getting-started/installation.md": "c93346e05d241b4f2967ed1ea4ef600286320025", "docs/core/getting-started/mutations.md": "2dbc68d4c23d8fdf068f423f05a30b954d11a17b", diff --git a/website/translate/lock/zh-Hans.json b/website/translate/lock/zh-Hans.json index 7a29d00058bd..dada727137b6 100644 --- a/website/translate/lock/zh-Hans.json +++ b/website/translate/lock/zh-Hans.json @@ -43,7 +43,7 @@ "docs/core/concepts/reactivity.md": "1f3f4c5c445db48d04de10966da56a9946d246f8", "docs/core/concepts/validation.md": "5119ecd2fac7be8250dbc79c8980e082641a40a6", "docs/core/getting-started/agent-skills.md": "f856007e07c8dd63ec585286b77ff19433a0116c", - "docs/core/getting-started/data-dependency.md": "f7b70948425e2a7a10fde2cafee92e91d11a1cb9", + "docs/core/getting-started/data-dependency.md": "db40ba054969b3ee7dc90939030594f5d664dc18", "docs/core/getting-started/debugging.md": "d2b5c1e8e5faec00320384a359facc15532129d3", "docs/core/getting-started/installation.md": "c93346e05d241b4f2967ed1ea4ef600286320025", "docs/core/getting-started/mutations.md": "2dbc68d4c23d8fdf068f423f05a30b954d11a17b", From 92efdbe52e4b8e92f06290c0fc1ec915f98c2814 Mon Sep 17 00:00:00 2001 From: Claude Date: Sat, 24 Oct 2026 23:38:18 +0000 Subject: [PATCH 4/5] docs: Keep the Field Notes app's list and header out of skill references Following #4458, the boundary demo's files that only make the preview work (the React layout's header and both frameworks' post lists) are marked docs-only, so skill references show just the boundary and the components it wraps. Vue's Layout.vue stays: its RouterView Suspense is the route's boundary. Co-Authored-By: Claude Opus 5.5 (1M context) Claude-Session: https://claude.ai/code/session_018FerSYMdRyzxaKQdv64mGf --- .../references/data-dependency.md | 37 ------------------- .../references/data-dependency.md | 37 ------------------- .../references/data-dependency.vue.md | 23 ------------ .../references/data-dependency.md | 23 ------------ docs/core/getting-started/data-dependency.md | 6 +-- .../core/getting-started/data-dependency.md | 6 +-- .../core/getting-started/data-dependency.md | 6 +-- .../core/getting-started/data-dependency.md | 6 +-- website/translate/lock/es.json | 2 +- website/translate/lock/pt-BR.json | 2 +- website/translate/lock/zh-Hans.json | 2 +- 11 files changed, 15 insertions(+), 135 deletions(-) diff --git a/.agents/skills/data-client-react/references/data-dependency.md b/.agents/skills/data-client-react/references/data-dependency.md index 9cf3b35874fe..58ca9878f252 100644 --- a/.agents/skills/data-client-react/references/data-dependency.md +++ b/.agents/skills/data-client-react/references/data-dependency.md @@ -184,43 +184,6 @@ export const CommentResource = resource({ }); ``` -```tsx title="Layout" layout -export default function Layout({ children }: { children: React.ReactNode }) { - return ( - <> -
-

Field Notes

-
- {children} - - ); -} -``` - -```tsx title="PostList" route="/" -import { useSuspense } from '@data-client/react'; -import { PostResource } from './Resources'; - -export default function PostList() { - const posts = useSuspense(PostResource.getList); - return ( -
- {posts.map(post => ( -
- -
-

- {post.title} -

- {post.author} -
-
- ))} -
- ); -} -``` - ```tsx title="PostPage" route="/posts/:id" {7,10} import { AsyncBoundary } from '@data-client/react'; import CommentList from './CommentList'; diff --git a/.agents/skills/data-client-rest/references/data-dependency.md b/.agents/skills/data-client-rest/references/data-dependency.md index 67e56dc89724..7cbcb5125d20 100644 --- a/.agents/skills/data-client-rest/references/data-dependency.md +++ b/.agents/skills/data-client-rest/references/data-dependency.md @@ -184,43 +184,6 @@ export const CommentResource = resource({ }); ``` -```tsx title="Layout" layout -export default function Layout({ children }: { children: React.ReactNode }) { - return ( - <> -
-

Field Notes

-
- {children} - - ); -} -``` - -```tsx title="PostList" route="/" -import { useSuspense } from '@data-client/react'; -import { PostResource } from './Resources'; - -export default function PostList() { - const posts = useSuspense(PostResource.getList); - return ( -
- {posts.map(post => ( -
- -
-

- {post.title} -

- {post.author} -
-
- ))} -
- ); -} -``` - ```tsx title="PostPage" route="/posts/:id" {7,10} import { AsyncBoundary } from '@data-client/react'; import CommentList from './CommentList'; diff --git a/.agents/skills/data-client-rest/references/data-dependency.vue.md b/.agents/skills/data-client-rest/references/data-dependency.vue.md index f3e4b62a0678..79d12df75867 100644 --- a/.agents/skills/data-client-rest/references/data-dependency.vue.md +++ b/.agents/skills/data-client-rest/references/data-dependency.vue.md @@ -207,29 +207,6 @@ export const CommentResource = resource({ ``` -```html title="PostList.vue" route="/" - - - -``` - ```html title="PostPage.vue" route="/posts/:id" {9,15} - - -``` - ```html title="PostPage.vue" route="/posts/:id" {9,15}