Blog author
The articles written by one author, with a sidebar of categories and load-more on scroll.
Framework: TanStack Start
This page is written for TanStack Start, the default and currently supported framework. See what depends on the framework.
The customer sees the articles written by one author, under the author's name, and more load as they scroll. Author links appear on article cards and the article page. Customise this route to change the list layout or the title. Unlike the other blog pages, the URL has no slug.
URL
| URL | Example |
|---|---|
/blog/author/$id | /blog/author/42 |
/{locale}/blog/author/$id | /ar/blog/author/42 |
Params
| Name | Type | Example | Description |
|---|---|---|---|
id | string | 42 | The author id. Use author.url from the API rather than building the URL by hand. |
locale | string | undefined | ar | Optional (locale prefix). |
Search params
None. Paging is not in the URL.
Route file
The engine writes this into app/routes/blog_.author.$id.tsx.
// @auto-generated
import { createFileRoute } from '@tanstack/react-router';
import { BlogAuthorRoute } from '@salla.sa/twilight-theme-engine/routes/blog';
import type { BlogAuthorLoaderData } from '@salla.sa/twilight-theme-engine/routes/blog';
import { BlogSkeleton } from '@salla.sa/twilight-theme-engine/skeleton';
import { withHead } from '@salla.sa/twilight-theme-engine/tanstack';
/**
* BlogAuthorRoute route configuration.
* Loads page data via loader and renders the BlogAuthorRoute component.
*/
export const Route = createFileRoute('/{-$locale}/blog/author/$id')({
loader: ({ params }): Promise<BlogAuthorLoaderData> =>
BlogAuthorRoute.loader({ params: { id: params.id }, locale: params.locale }),
head: withHead(BlogAuthorRoute),
pendingComponent: () => <BlogSkeleton />,
component: BlogAuthorRouteComponent,
});
function BlogAuthorRouteComponent() {
const data: BlogAuthorLoaderData = Route.useLoaderData();
return <BlogAuthorRoute.Component {...data} />;
}Delete the // @auto-generated line to own the file. Keep the path in createFileRoute.
A customised version
This keeps the engine's loader, head and skeleton and draws a plain list of the first batch.
import { createFileRoute } from '@tanstack/react-router';
import { BlogAuthorRoute } from '@salla.sa/twilight-theme-engine/routes/blog';
import type { BlogAuthorLoaderData } from '@salla.sa/twilight-theme-engine/routes/blog';
import { BlogSkeleton } from '@salla.sa/twilight-theme-engine/skeleton';
import { withHead } from '@salla.sa/twilight-theme-engine/tanstack';
import { HookSlot } from '@salla.sa/twilight-theme-engine/hooks';
import { Breadcrumb, Link, NoContent } from '@salla.sa/twilight-theme-engine/components/common';
export const Route = createFileRoute('/{-$locale}/blog/author/$id')({
loader: ({ params }): Promise<BlogAuthorLoaderData> =>
BlogAuthorRoute.loader({ params: { id: params.id }, locale: params.locale }),
head: withHead(BlogAuthorRoute),
pendingComponent: () => <BlogSkeleton />,
component: AuthorPage,
});
function AuthorPage() {
const { page, author, articles }: BlogAuthorLoaderData = Route.useLoaderData();
return (
<div className="container">
<Breadcrumb page={page} />
<HookSlot name="blog:author.start" />
<h1>{author.name}</h1>
{articles.length === 0 ? (
<NoContent icon="sicon-inbox" message="No articles yet" />
) : (
<ul>
{articles.map((a) => (
<li key={a.id}>
<Link to={a.url}>{a.name}</Link>
</li>
))}
</ul>
)}
<HookSlot name="blog:author.end" />
</div>
);
}What it loads
BlogAuthorRoute.loader returns BlogAuthorLoaderData:
| Field | Type | What it is |
|---|---|---|
page | Page | title and id are the author's name; breadcrumbs Home, Blog, author |
author | BlogAuthor | name and url only |
categories | BlogCategory[] | All categories, for the sidebar |
articles | ArticleSummary[] | The first batch of articles |
cursor | Pagination | next, previous, current |
It makes two requests at once: this author's articles and the blog overview (for the sidebar), through the blog API module. The first request is rendered on the server. Further batches are fetched only in the browser.
Hook slots: blog:author.start, blog:author.end.
Head and SEO
BlogAuthorRoute.head uses the author's name as the title, the English text Articles by {name} as the description, and hreflang links. It sets no canonical link, and the description is not translated; override either with withHead(BlogAuthorRoute, (result) => ({ ...result, ... })). See SEO and head.
Edge cases
- More articles than one batch. When
cursor.nextis set, the stock page loads the next batches as the customer scrolls, in the browser only. Those later requests filter by the same URL$idas the first batch. A custom page that shows onlyarticlesshows only the first batch. - No articles. The page shows the "no articles" state and no heading.
- Unknown author. The loader throws a not-found error, so the customer sees the not-found page instead of a server error.
- Locale. The
localeyou pass to the loader is not used; the language follows the request.