# Journal

> Blog index layout with editable CONTENT posts.

A shadcn/ui block from the [@atroui](https://www.atroui.com) registry, listed on [shadcn.exchange](https://shadcn.exchange/r/atroui/journal-content).

- Page, with a live preview: https://shadcn.exchange/r/atroui/journal-content
- Screenshot: https://abzjhwgwpdpz7ua5.public.blob.vercel-storage.com/shots/atroui/journal-content-light-59a2314f32e6a506.png
- Registry item: https://www.atroui.com/r/journal-content.json
- Listed: 2026-10-13, source last changed 2026-10-13

## Install

```bash
npx shadcn@latest add https://www.atroui.com/r/journal-content.json
```

Run it in a project that has shadcn/ui set up. It adds the files below and installs what they depend on.

## Dependencies

- npm packages: `next`

## Files

### `registry/default/blocks/journal-content.tsx`

```tsx
"use client"

import Link from "next/link"
import { useMemo, useState } from "react"

/** Edit CONTENT.posts for your journal index. */
const CONTENT = {
  title: "Journal",
  blurb: "Notes on shipping product UI.",
  posts: [
    {
      slug: "own-the-ui",
      title: "Own the UI with the shadcn registry",
      description: "Why copy-into-repo beats black-box packages for marketing UI.",
      date: "2026-08-01",
    },
    {
      slug: "dark-first-tokens",
      title: "Dark-first tokens that survive light mode",
      description: "How AtroUI structures CSS variables for black-canvas products.",
      date: "2026-07-20",
    },
  ],
}

export function JournalContent() {
  const [q, setQ] = useState("")
  const posts = useMemo(() => {
    const needle = q.trim().toLowerCase()
    if (!needle) return CONTENT.posts
    return CONTENT.posts.filter(
      (p) =>
        p.title.toLowerCase().includes(needle) ||
        p.description.toLowerCase().includes(needle)
    )
  }, [q])

  return (
    <div className="space-y-8">
      <header>
        <p className="ms-stamp">{CONTENT.title}</p>
        <p className="mt-2 text-sm text-muted-foreground">{CONTENT.blurb}</p>
        <input
          value={q}
          onChange={(e) => setQ(e.target.value)}
          placeholder="Search posts…"
          className="mt-4 w-full max-w-md rounded-lg border border-border-subtle bg-background px-4 py-2 text-sm outline-none focus-visible:ring-2 focus-visible:ring-ring"
        />
      </header>
      <ul className="divide-y divide-border-subtle border-y border-border-subtle">
        {posts.map((post) => (
          <li key={post.slug}>
            <Link
              href={`/journal/${post.slug}`}
              className="block py-5 transition-colors hover:bg-muted/20"
            >
              <p className="font-mono text-[11px] text-muted-foreground">
                {post.date}
              </p>
              <h3 className="mt-1 text-lg font-medium text-foreground">
                {post.title}
              </h3>
              <p className="mt-1 text-sm text-muted-foreground">
                {post.description}
              </p>
            </Link>
          </li>
        ))}
      </ul>
    </div>
  )
}
```
