# Blogpost 2 - Article with sticky sidebar

> A two-column blog article with a sticky narrow column for back navigation, display title, and avatar byline beside a main column with hero image and full prose.

A shadcn/ui block from the [@shadcnblocks](https://shadcnblocks.com) registry, listed on [shadcn.exchange](https://shadcn.exchange/r/shadcnblocks/blogpost2).

- Page, with a live preview: https://shadcn.exchange/r/shadcnblocks/blogpost2
- Screenshot: https://abzjhwgwpdpz7ua5.public.blob.vercel-storage.com/shots/shadcnblocks/blogpost2-light-4af8c9131d06cd16.png
- Registry item: https://shadcnblocks.com/r/blogpost2.json
- Listed: 2026-10-13, source last changed 2026-10-13

## Install

```bash
npx shadcn@latest add https://shadcnblocks.com/r/blogpost2.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: `cn`, `lucide-react`
- Registry items: `avatar`

## Files

### `block/blogpost2/blogpost2.tsx`

```tsx
import {
  ChevronLeft,
  Facebook,
  Home,
  Link2,
  Linkedin,
  Twitter,
} from "lucide-react";
import { cn } from "cn";

import { Avatar, AvatarImage } from "@/components/ui/avatar";

interface BlogPostAuthor {
  name: string;
  role?: string;
  image?: Image;
  href?: string;
  website?: string;
  websiteName?: string;
  bio?: string;
}
interface Image {
  src: string;
  alt: string;
  srcDark?: string;
}

interface BlogPostArticleProps {
  title: string;
  date?: string;
  image?: Image;
  body?: React.ReactNode;
  author?: BlogPostAuthor;
  className?: string;
}

interface Blogpost2Props extends BlogPostArticleProps {
  backLink?: {
    text: string;
    url: string;
  };
}
type Props = Partial<Blogpost2Props>;

const person1 = {
  name: "Jack Walsh",
  firstName: "Jack",
  lastName: "Walsh",
  initials: "JW",
  email: "jack.walsh@example.com",
  username: "jackwalsh",
  role: "Product Designer",
  company: "Cartwright",
  location: "Brisbane, Australia",
  avatar: "https://cdn.shadcnblocks.com/shadcnblocks/image-set/modern/avatars/avatar1.webp",
};
const defaultProps: BlogPostArticleProps = {
  title: "Building the perfect blog post with shadcn/ui",
  date: "March 18, 2026",
  image: {
    src: "https://cdn.shadcnblocks.com/shadcnblocks/image-set/modern/blog/photo-1-16x9.jpg",
    alt: "A white building with vertical fins against a clear sky",
  },
  body: (
    <>
      <h2 id="section1" className="scroll-mt-24">
        I was tired of fighting my UI library
      </h2>
      <p>
        I have rebuilt my blog four times in six years. Each time I picked a
        popular component library, and each time I ended up in the same place,
        rebuilding many components from scratch.
      </p>
      <p>
        So when people started talking about <a href="#">shadcn/ui</a>, I was
        skeptical. Another library? But it is <strong>not a library</strong>.
        You run a command, and the source code for a button lands in your
        project. Not a package. Not a wrapper. <em>The actual file.</em>
      </p>
      <p>
        That sounds like a small detail. It is not. It changes who is in charge
        of your UI, and it took me a few weeks to realize how much I had been
        designing around other people&apos;s decisions.
      </p>
      <h3>What owning the code actually means</h3>
      <p>
        With a traditional library, customization is a negotiation. You read the
        docs, find the right prop, and hope the maintainers anticipated your use
        case. With shadcn/ui, you open the file and change it.
      </p>
      <ul>
        <li>
          <strong>No version lock-in.</strong> Upgrades are opt-in, one
          component at a time.
        </li>
        <li>
          <strong>No theme API to learn.</strong> Styling is plain{" "}
          <a href="#">Tailwind CSS</a> and CSS variables.
        </li>
        <li>
          <strong>Accessibility built in.</strong> Under the hood:
          <ul>
            <li>Radix UI or Base UI handle focus and keyboard behavior</li>
            <li>ARIA attributes are already wired up</li>
          </ul>
        </li>
      </ul>
      <p>
        I used to think the hard part of a UI library was the components.{" "}
        <strong>I was wrong</strong>. The hard part was finding the right prop
        accepting that I could just edit the file.
      </p>
      <blockquote>
        <p>
          The best component library is the one you can delete a line from
          without asking anyone for permission.
        </p>
      </blockquote>
      <h2 id="section2" className="scroll-mt-24">
        Rebuilding the post page from scratch
      </h2>
      <p>
        The page I cared about most was the article itself. The one you are
        reading right now. I wanted a calm reading column, a sticky table of
        contents, and typography that did not need babysitting.
      </p>
      <h3>Getting set up</h3>
      <ol>
        <li>
          Initialize the project with <code>npx shadcn@latest init</code>.
        </li>
        <li>Add the handful of components the page actually needs.</li>
        <li>
          Pull in a starting layout from <a href="#">Shadcnblocks</a> instead of
          a blank file.
        </li>
      </ol>
      <pre>
        <code>{`npx shadcn@latest add avatar badge separator

# Added
components/ui/avatar.tsx
components/ui/badge.tsx
components/ui/separator.tsx`}</code>
      </pre>
      <p>
        Three components, three files, all of them readable in under a minute. I
        opened <code>badge.tsx</code>, tweaked the radius, and moved on. No
        config file, no theme provider, no <abbr title="Pull request">PR</abbr>{" "}
        to someone else&apos;s repo.
      </p>
      <p>
        Small quality-of-life win: the docs have a command menu. Press{" "}
        <kbd>⌘</kbd> + <kbd>K</kbd>, type a component name, and you are there.
      </p>
      <table>
        <thead>
          <tr>
            <th>What changed</th>
            <th>Old blog</th>
            <th>shadcn/ui rebuild</th>
          </tr>
        </thead>
        <tbody>
          <tr>
            <td>UI dependencies</td>
            <td>1 large package</td>
            <td>Copied source files</td>
          </tr>
          <tr>
            <td>Style overrides</td>
            <td>Dozens</td>
            <td>None</td>
          </tr>
          <tr>
            <td>Dark mode</td>
            <td>A plugin and a prayer</td>
            <td>CSS variables</td>
          </tr>
          <tr>
            <td>Time to first post</td>
            <td>Two weekends</td>
            <td>One afternoon</td>
          </tr>
        </tbody>
      </table>
      <h2>Good component systems work the same way</h2>
      <p>
        Do not install every component on day one. Add them as you need them.
        The whole point is a codebase you understand, and that is easier when it
        grows with the project.<sup>1</sup>
      </p>
      <figure>
        <img
          src="https://cdn.shadcnblocks.com/shadcnblocks/image-set/modern/blog/photo-4-16x9.jpg"
          alt="Curved concrete balconies on a building against a blue sky"
          className="aspect-video w-full rounded-md object-cover"
        />
        <figcaption>
          Repeated parts, one coherent building. Good component systems work the
          same way.
        </figcaption>
      </figure>
      <p>
        A few terms kept coming up while I worked, so here is how I ended up
        thinking about them:
      </p>
      <p>
        Do not treat the defaults as sacred either. They are a starting point,
        not a spec. Change things. That is the deal you signed up for.
      </p>
      <p>
        And if you want to skip the blank page entirely, start from a finished
        layout and edit it down. That is what I did with this one. If you build
        something with it, I would love to see it. Find me on <a href="#">X</a>{" "}
        or drop a link in the <a href="#">comments</a>.
      </p>
      <p>
        <small>
          <sup>1</sup> I installed every component on day one. Learn from my
          mistakes.
        </small>
      </p>
    </>
  ),
  author: {
    name: person1.name,
    role: person1.role,
    href: "#",
    image: {
      src: person1.avatar,
      alt: person1.name,
    },
    bio: `${person1.firstName} writes about shadcn/ui, front-end craft, and rebuilding the same blog more times than is reasonable.`,
  },
};

const Blogpost2 = (props: Props) => {
  const { className, title, author, image, date, backLink, body } = {
    ...defaultProps,
    ...props,
  };
  return (
    <section className={cn("py-32", className)}>
      <div className="container">
        <div className="relative flex flex-col justify-between gap-10 lg:flex-row">
          <aside className="top-10 h-fit flex-shrink-0 lg:sticky lg:w-[300px] xl:w-[400px]">
            <a
              className="mb-5 flex items-center gap-1 text-muted-foreground hover:text-primary"
              href={backLink?.url}
            >
              <ChevronLeft className="h-full w-4" />
              {backLink?.text}
            </a>
            <h1 className="mb-5 text-3xl font-bold text-balance lg:text-4xl">
              {title}
            </h1>
            <div className="flex gap-3">
              <Avatar className="size-7 rounded-full">
                <AvatarImage
                  src={author?.image?.src}
                  alt={author?.image?.alt}
                />
              </Avatar>
              <div>
                <h2 className="font-semibold">{author?.name}</h2>
                <p className="text-xs text-muted-foreground">{date}</p>
              </div>
            </div>
          </aside>

          <article className="">
            <img
              src={image?.src}
              alt={image?.alt}
              className="mt-0 mb-8 aspect-video w-full rounded-lg object-cover"
            />
            <div className="prose dark:prose-invert">{body}</div>
          </article>
        </div>
      </div>
    </section>
  );
};

export { Blogpost2 };
```
