Repository navigation
feat(content): add public list pages to the Content Engine - #870
aXenDeveloper wants to merge 2 commits into
Conversation
`delivery.list` gives a content type a public listing page, such as `/blog`, with pagination, search and filters read from the URL: - `defineContentType` validates the list path (static, not under /admin or /api), the page size (1-50) and that every filter is in `publicApi.filterableFields`. The build fails with `content-url-without-page` when no page serves the list URL. - `@vitnode/core/tanstack/content` adds `contentListSearch`, `contentListQuery`, `contentListPage` and `contentListPageHead`, plus `ContentList`, `ContentCard`, `ContentListEmpty`, `ContentListPagination`, `ContentListSearchForm` and `ContentListFilter`. - Only the plain first page is indexed, with canonical and hreflang links. Later pages, searches and filters answer `noindex, follow`, a robots value plugin routes can now return. A page past the end answers 404. - The blog gets `/blog` and the example plugin `/articles` with a "Featured" filter. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DChMEKwVTEBMNHcKKJ1T9u
Adds "Show a list of records", a step-by-step guide that continues the recipes example with `delivery.list`, a list page with search, filters and pagination, and the SEO each URL gets. Screenshots are real captures of the page built from the guide's code. "List page reference" lists every helper and component, and the content type reference, the overview and the delivery guide link to both. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DChMEKwVTEBMNHcKKJ1T9u
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
|
React Doctor found 2 new issues in 2 files · 2 warnings · score 91 / 100 (Great) · 0 fixed · vs 2 warnings
Reviewed by React Doctor for commit |
|
|
||
| const filters = names.map(name => { | ||
| const target = resolveSeoTarget(fields, name); | ||
| if (!publicApi.filterableFields.includes(name) || target === null) { |
There was a problem hiding this comment.
React Doctor · react-doctor/js-set-map-lookups (warning)
This scales poorly because array.includes() inside a loop scans the whole list every time. Use a Set for constant-time lookups.
Fix → Use a Set or Map when you check for the same items over and over. Array.includes/find scans the whole list each time
| ); | ||
|
|
||
| return ( | ||
| <form |
There was a problem hiding this comment.
React Doctor · react-doctor/no-prevent-default (warning)
Your users can't submit this
without JavaScript because onSubmit calls preventDefault(), so use a server action like<form action={serverAction}> to make it work either way.
Fix → Use <form action> where your framework supports it (it works without JS), or use a <button> instead of an <a> with preventDefault.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 911dc6de4c
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| load: async ({ context, search }) => | ||
| await loadPosts({ locale: context.locale, search }), |
There was a problem hiding this comment.
Cache list loads through the route query client
On client navigation, this loader calls fetcher directly instead of warming data through context.queryClient.query, so identical list requests cannot be shared or deduplicated with preloads or other consumers and are fetched again when the route reloads. The new articles list repeats the same pattern in plugins/example/src/pages/articles-page.tsx; both should use the TanStack plugin-route authoring API that exposes queryClient and query options keyed by locale and search.
AGENTS.md reference: AGENTS.md:L52-L52
Useful? React with 👍 / 👎.
|
|
||
| type BlogPostsPageData = Awaited<ReturnType<typeof loadPosts>>; | ||
|
|
||
| export const route = definePluginRoute<BlogPostsPageData, ContentListSearch>({ |
There was a problem hiding this comment.
Declare breadcrumbs for the new list pages
This new page route does not declare route.breadcrumb, so MainBreadcrumb has no current-page crumb for /blog; the new /articles page has the same omission. Add breadcrumb declarations to both route modules, using their declared message namespaces for translated labels.
AGENTS.md reference: AGENTS.md:L30-L30
Useful? React with 👍 / 👎.
| const filters = names.map(name => { | ||
| const target = resolveSeoTarget(fields, name); | ||
| if (!publicApi.filterableFields.includes(name) || target === null) { |
There was a problem hiding this comment.
Reject pagination keys from list filters
When a content type exposes a filterable field named page, this check accepts it because page is currently absent from RESERVED_FILTER_KEYS. contentListSearch then stores the field value in its reserved pagination property and contentListQuery emits it as the API's page parameter; for example, a boolean filter generates ?page=true, which zodPaginationQuery rejects with a 400 and the page surfaces as a loader error. Reserve page globally or reject it from delivery.list.filters before building the resolved filter.
Useful? React with 👍 / 👎.
delivery.listgives a content type a public listing page, such as/blog,with pagination, search and filters read from the URL:
defineContentTypevalidates the list path (static, not under /admin or/api), the page size (1-50) and that every filter is in
publicApi.filterableFields. The build fails withcontent-url-without-pagewhen no page serves the list URL.@vitnode/core/tanstack/contentaddscontentListSearch,contentListQuery,contentListPageandcontentListPageHead, plusContentList,ContentCard,ContentListEmpty,ContentListPagination,ContentListSearchFormandContentListFilter.Later pages, searches and filters answer
noindex, follow, a robots valueplugin routes can now return. A page past the end answers 404.
/blogand the example plugin/articleswith a"Featured" filter.
Co-Authored-By: Claude Opus 5.5 noreply@anthropic.com
Claude-Session: https://claude.ai/code/session_01DChMEKwVTEBMNHcKKJ1T9u