import { type ElementType, type ReactElement } from "react"; import type { MDXRemoteProps } from "next-mdx-remote/rsc"; import type { Blog } from "../../blog/blog.js"; /** * Props for {@link BlogPage}. */ export interface BlogPageProps { /** The configured `Blog` instance. The post and site config are derived from it. */ blog: Blog; /** The slug to render; the post is loaded via `blog.getPost(slug, lang)`. */ slug: string; /** Which language to render the post in. Defaults to the blog's default locale. */ lang?: string; /** Route the blog is mounted at; used for the back-link and similar-post links. Defaults to `blog.site.basePath` (or `/blog`). */ basePath?: string; /** Custom component map for MDX elements, forwarded to `MDXRemote` (e.g. code highlighting). */ components?: MDXRemoteProps["components"]; /** * EXTRA remark/rehype options forwarded to `MDXRemote` (e.g. `rehype-pretty-code`). GFM * (tables, strikethrough, task lists, autolinks) is already on by default - plugins passed * here are merged with it, never replace it. See `shared/mdx.ts`. */ mdxOptions?: MDXRemoteProps["options"]; /** Whether to render the "back to blog" link above the title. Defaults to `true`. */ showBackLink?: boolean; /** Label for the back-link. Defaults to the `lang` translation (e.g. `"← Blog"`). */ backLabel?: string; /** Element used for the hero image. Defaults to `"img"`; pass e.g. `next/image` to opt into it. */ imgComponent?: ElementType; /** Element used for the back-link and similar-post links. Defaults to `"a"`; pass `next/link`. */ linkComponent?: ElementType; /** Whether to render the right-side sidebar (heading minimap, reading time, similar posts). Defaults to `true`. */ showSidebar?: boolean; /** Heading for the sidebar's minimap, doubling as its mobile toggle. Defaults to the `lang` translation (e.g. `"On this page"`). */ tocTitle?: string; /** Heading for the sidebar's similar-post list. Defaults to the `lang` translation (e.g. `"Similar pages"`). */ similarTitle?: string; /** Maximum number of similar posts to show in the sidebar. Defaults to `3`. */ similarCount?: number; /** Formats the reading-time label from the estimated minutes. Defaults to the `lang` translation (e.g. ``(m) => `${m} min read` ``). */ readingLabel?: (minutes: number) => string; /** Formats the "Updated " meta item from the formatted date. Defaults to the `lang` translation. */ updatedLabel?: (date: string) => string; } /** * Renders a single blog post: an optional back-link, the title, the formatted date, the hero * image, and the MDX body (compiled with `next-mdx-remote`, memoized per source+options - see `shared/mdx.ts`) inside a `.scribekit-prose` * container, with an "Updated " item beside the publish date whenever the post's `updated` * front-matter is later than its `date`, alongside a right-side sidebar with a heading minimap (table of contents), the * estimated reading time, and "Similar pages". When the post declares an `author`, their name * appears in the meta row (with a small round avatar when `author-image` is set) and a plain * "Written by" author bio - a divider, the avatar (when set), the name, and the category / * publish date - closes the article. With `author-url` set, the name in both places is a link * rendered through `linkComponent` (so a Next `Link` prefetches it like any other internal link); * without it the name stays plain text. * * A server component: pass your configured `Blog` instance and the slug (and, for a translated * post, the `lang`), and it loads the post (`blog.getPost(slug, lang)`), formats the date in that * language's locale, computes the minimap / reading time / same-language similar posts, and * derives the SEO JSON-LD from `blog.site`. A language switcher is deliberately not rendered - it * is the consumer's to build (from `blog.getTranslations(slug)`, `blog.slugFor(slug, lang)` and * `localePath`). The sidebar * itself is a client component (`BlogSidebar`) for its scroll-spy and mobile collapse; the * similar-post cards are rendered here (server-side) so they keep your `linkComponent` / * `imgComponent`, and passed into it. `##`/`###` headings in the MDX body get anchor ids * injected so the minimap can jump to them - if you override `h2`/`h3` in `components`, add * your own ids. Supply your own navbar/footer/CTA around it. Requires the package stylesheet. * * With the recommended route setup (`generateStaticParams` + `dynamicParams = false`), unknown * slugs 404 at the router before this renders, so `getPost` only ever sees valid slugs. If you * opt into dynamic rendering, guard with your own `getPost` try/catch -> `notFound()` in the * page component before rendering. * * @param props - see {@link BlogPageProps}. * @returns the blog post article, with the sidebar when enabled and there is content for it. */ export declare function BlogPage({ blog, slug, lang, basePath, components, mdxOptions, showBackLink, backLabel, imgComponent: Img, linkComponent: Link, showSidebar, tocTitle, similarTitle, similarCount, readingLabel, updatedLabel, }: BlogPageProps): ReactElement;