# Blog author (https://docs.salla.dev/twilight/react/routes/blog-author)



<Callout title="Framework: TanStack Start">
  This page is written for TanStack Start, the default and currently supported framework. [See what depends on the framework](https://docs.salla.dev/twilight/react/start/frameworks).
</Callout>

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]

| URL                         | Example              |
| --------------------------- | -------------------- |
| `/blog/author/$id`          | `/blog/author/42`    |
| `/{locale}/blog/author/$id` | `/ar/blog/author/42` |

### Params [#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](https://docs.salla.dev/twilight/react/routes/home#locale-prefix)).                            |

### Search params [#search-params]

None. Paging is not in the URL.

## Route file [#route-file]

The engine writes this into `app/routes/blog_.author.$id.tsx`.

```tsx title="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 [#a-customised-version]

This keeps the engine's loader, head and skeleton and draws a plain list of the first batch.

```tsx title="app/routes/blog_.author.$id.tsx"
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 [#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](https://docs.salla.dev/twilight/react/api/modules/blog). The first request is rendered on the server. Further batches are fetched only in the browser.

[Hook slots](https://docs.salla.dev/twilight/react/guide/hooks-and-slots): `blog:author.start`, `blog:author.end`.

## Head and SEO [#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](https://docs.salla.dev/twilight/react/guide/seo-and-head).

## Edge cases [#edge-cases]

* **More articles than one batch.** When `cursor.next` is set, the stock page loads the next batches as the customer scrolls, in the browser only. Those later requests filter by the same URL `$id` as the first batch. A custom page that shows only `articles` shows 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](https://docs.salla.dev/twilight/react/routes/not-found) instead of a server error.
* **Locale.** The `locale` you pass to the loader is not used; the language follows the request.

## See also [#see-also]

* [Blog](https://docs.salla.dev/twilight/react/routes/blog), [Blog article](https://docs.salla.dev/twilight/react/routes/blog-article), [Blog category](https://docs.salla.dev/twilight/react/routes/blog-category)
* [Fork a built-in route](https://docs.salla.dev/twilight/react/recipes/fork-a-built-in-route)
* [Routes overview](https://docs.salla.dev/twilight/react/routes)
