Launch Journal
A Paragraph CMS-native launch feed with featured posts, labels, search, authors, and pagination.
Launch Journal renders a release-style blog surface with featured cards, responsive category navigation, mobile search, multi-author avatars, excerpts, RSS action, and load-more pagination.
Installation
pnpm dlx shadcn@latest add paragraphcms/shadcn-registry/launch-journalnpx shadcn@latest add paragraphcms/shadcn-registry/launch-journalyarn dlx shadcn@latest add paragraphcms/shadcn-registry/launch-journalbunx --bun shadcn@latest add paragraphcms/shadcn-registry/launch-journalInstall the runtime dependencies:
pnpm add @paragraphcms/client @tabler/icons-react clsx tailwind-merge @base-ui/react class-variance-authoritynpm install @paragraphcms/client @tabler/icons-react clsx tailwind-merge @base-ui/react class-variance-authorityyarn add @paragraphcms/client @tabler/icons-react clsx tailwind-merge @base-ui/react class-variance-authoritybun add @paragraphcms/client @tabler/icons-react clsx tailwind-merge @base-ui/react class-variance-authorityAdd these registry files to your project:
ui/launch-journal.tsxui/avatar.tsxui/badge.tsxui/button.tsxui/input.tsxlib/utils.ts
Keep the aliases aligned with your components.json: @/components/ui/* and @/lib/utils.
Prerequisites
You need a Paragraph CMS API key available only on the server:
PARAGRAPH_API_KEY=your_api_keyCreate a server-side Paragraph CMS client:
import { Client } from "@paragraphcms/client";
const apiKey = process.env.PARAGRAPH_API_KEY;
if (!apiKey) {
throw new Error("PARAGRAPH_API_KEY environment variable is not set");
}
export const client = new Client({ apiKey });Launch Journal expects PageSummaryWithSlug[]. It reads labels from post.labels, excerpts from post.fields.excerpt or post.fields.summary, and optional multi-author data from post.fields.authors.
Usage
Fetch your posts and split the first items into featured_posts.
import { LaunchJournal } from "@/components/ui/launch-journal";
import { client } from "@/paragraph.config";
export default async function BlogPage() {
const { data: posts, error } = await client.pages.list({
collection: "blog",
requiredSlug: true,
});
if (error) {
throw error;
}
return (
<LaunchJournal
featured_posts={posts.slice(0, 3)}
posts={posts.slice(3)}
rss_href="/blog/rss.xml"
/>
);
}If you omit categories, the component builds its category navigation from the labels present on featured_posts and posts.