Skip to content
Facade UI
blocktemplate

Changelog page

A complete changelog page built from Facade UI sections: a header, the releases with their changes, and an optional email sign-up. You supply one content object.

Open in v0 (opens in a new tab)

Preview

Open full width (opens in a new tab)
Preview width

Installation

Package manager
pnpm dlx shadcn@latest add @facade/changelog

This component needs changelog-list, container, cta-group, footer, nav-top, newsletter, section, section-header, types. The CLI installs them for you.

Usage

This is the code for the preview above. To install it as a file, run npx shadcn@latest add @facade/changelog-demo.

demos/changelog.tsx
import { Changelog } from "@registry/templates/changelog"

import { FOOTER_GROUPS, NAV_ITEMS, RELEASES } from "./content"

export function Demo() {
  return (
    <div className="-m-6 sm:-m-10">
      <Changelog
        currentPath="#changelog"
        content={{
          brand: "Facade UI",
          nav: [...NAV_ITEMS, { label: "Changelog", href: "#changelog" }],
          navActions: [{ label: "Get started", href: "#start" }],
          header: {
            eyebrow: "Changelog",
            title: "What is new in Facade UI",
            description: "New sections, fixes and the occasional breaking change.",
            actions: [
              { label: "RSS feed", href: "#rss", variant: "outline" },
              {
                label: "GitHub releases",
                href: "https://github.com",
                external: true,
                variant: "ghost",
              },
            ],
          },
          releases: RELEASES,
          subscribe: {
            title: "Get the next release by email",
            description: "One email per release. Nothing else.",
          },
          footer: {
            groups: FOOTER_GROUPS,
            copyright: "© 2026 Facade UI contributors",
          },
        }}
      />
    </div>
  )
}

Source

These are the files the CLI copies into your project.

components/templates/changelog.tsx
/**
 * Changelog — a release history page, assembled from registry sections.
 *
 * A page header, the releases, and an optional email sign-up for people who
 * want the next one in their inbox. Like the other templates it is
 * composition: `ChangelogList` does the work and installs on its own too.
 *
 * a11y: the page title is the only `<h1>`; each release is an `<h2>` inside
 * its own `<article>`, so heading navigation steps release by release. The
 * sign-up reports its outcome through `Newsletter`'s live region.
 *
 * Dependencies: react, @/lib/types, @/components/sections/*,
 * @/components/ui/*.
 */

import type { ReactNode } from "react"

import type { CtaItem, LinkComponent } from "@/lib/types"
import {
  ChangelogList,
  type ChangeKind,
  type ChangelogEntry,
} from "@/components/sections/changelog-list"
import { Footer, type FooterGroup, type FooterLink } from "@/components/sections/footer"
import { NavTop, type NavItem } from "@/components/sections/nav-top"
import { Newsletter, type NewsletterStatus } from "@/components/sections/newsletter"
import { Container } from "@/components/ui/container"
import { CtaGroup } from "@/components/ui/cta-group"
import { Section } from "@/components/ui/section"
import { SectionHeader } from "@/components/ui/section-header"

export interface ChangelogContent {
  brand: ReactNode
  nav: NavItem[]
  navActions?: CtaItem[]

  header: {
    eyebrow?: string
    title: string
    description?: string
    /** Links beside the title, such as an RSS feed or the GitHub releases. */
    actions?: CtaItem[]
  }

  /** Newest first. */
  releases: ChangelogEntry[]
  /** Words for the change kinds, for translation. */
  kindLabels?: Partial<Record<ChangeKind, string>>

  subscribe?: {
    eyebrow?: string
    title: string
    description?: string
    note?: ReactNode
  }

  footer: {
    groups?: FooterGroup[]
    brand?: ReactNode
    copyright?: ReactNode
    legal?: FooterLink[]
  }
}

export interface ChangelogProps {
  content: ChangelogContent
  link?: LinkComponent
  /** Marks the active nav item. */
  currentPath?: string
  /** Shown above the header. Usually a `Banner`. */
  banner?: ReactNode
  /** Wire this to your own form action or mutation. */
  onSubscribe?: (email: string) => void
  subscribeStatus?: NewsletterStatus
  subscribeError?: string
}

export function Changelog({
  content,
  link,
  currentPath,
  banner,
  onSubscribe,
  subscribeStatus,
  subscribeError,
}: ChangelogProps) {
  const { header, releases, subscribe, footer } = content

  return (
    <>
      {banner}

      <NavTop
        brand={content.brand}
        items={content.nav}
        actions={content.navActions}
        link={link}
        currentPath={currentPath}
      />

      <main id="main">
        {/* A div: the h1 names the page, so a second landmark adds nothing. */}
        <Section as="div" spacing="md" className="border-b">
          <Container size="md">
            <SectionHeader
              headingLevel={1}
              size="lg"
              eyebrow={header.eyebrow}
              title={header.title}
              description={header.description}
              actions={
                header.actions ? (
                  <CtaGroup items={header.actions} link={link} size="sm" />
                ) : undefined
              }
            />
          </Container>
        </Section>

        <ChangelogList
          items={releases}
          kindLabels={content.kindLabels}
          link={link}
          itemHeadingLevel={2}
          spacing="sm"
        />

        {subscribe ? (
          <Newsletter
            eyebrow={subscribe.eyebrow}
            title={subscribe.title}
            description={subscribe.description}
            note={subscribe.note}
            onSubmit={onSubscribe}
            status={subscribeStatus}
            errorMessage={subscribeError}
            headingLevel={2}
          />
        ) : null}
      </main>

      <Footer
        brand={footer.brand ?? content.brand}
        groups={footer.groups}
        copyright={footer.copyright}
        legal={footer.legal}
        link={link}
        headingLevel={2}
      />
    </>
  )
}

Props

ChangelogProps

Props for ChangelogProps
PropTypeDefault
content*RequiredChangelogContent—
linkLinkComponent—
currentPathMarks the active nav item.string—
bannerShown above the header. Usually a `Banner`.ReactNode—
onSubscribeWire this to your own form action or mutation.(email: string) => void—
subscribeStatusNewsletterStatus—
subscribeErrorstring—