Container component inspired by shadcn/ui and Tailwind UI

I've used shadcn/ui for a while now, and one thing I kept missing was a container component. I wanted something that handles responsive width and padding, so I don't have to think about it on every page.

The problem I kept running into

Every project needs containers. On a landing page or on a dashboard, I kept wrapping content in divs with max-w-7xl mx-auto px-4 sm:px-6 lg:px-8. It works, but I repeated it everywhere and sometimes got it wrong. I copied the same patterns from Tailwind UI examples and changed the padding and max-width in each new project.

What Tailwind UI does

Tailwind UI doesn't put one container class on everything. It uses a few specific patterns:

  • Full-width on mobile, constrained above
  • Always constrained with consistent padding
  • Breakpoint-specific constraints
  • Narrow layouts for content-heavy pages

Each pattern has a use. "Full-width on mobile" gives content the whole screen on a phone and keeps a readable width on larger screens.

Building the component

I used class-variance-authority to turn these patterns into variants. Each variant needs its own padding. Some start padding at sm:, others add mobile padding from the start.

Installation

Option 1: Install from the registry (recommended)

bunx shadcn@latest add https://registry.emreturan.dev/r/container.json

This installs the component and its dependencies in your project.

Option 2: Manual installation

Copy this code into your project:

import * as React from "react"
import { cva, type VariantProps } from "class-variance-authority"
import { cn } from "@/lib/utils"

const containerVariants = cva("mx-auto", {
  variants: {
    variant: {
      fullMobileConstrainedPadded: "max-w-7xl sm:px-6 lg:px-8",
      constrainedPadded: "max-w-7xl px-4 sm:px-6 lg:px-8",
      fullMobileConstrainedBreakpointPadded: "max-w-screen-xl sm:px-6 lg:px-8",
      constrainedBreakpointPadded: "max-w-screen-xl px-4 sm:px-6 lg:px-8",
      narrowConstrainedPadded: "max-w-3xl px-4 sm:px-6 lg:px-8",
    },
  },
  defaultVariants: {
    variant: "narrowConstrainedPadded",
  },
})

export interface ContainerProps extends React.HTMLAttributes<HTMLDivElement>, VariantProps<typeof containerVariants> {}

const Container = React.forwardRef<HTMLDivElement, ContainerProps>(
  ({ className, children, variant, ...props }, ref) => {
    return (
      <div ref={ref} className={cn(containerVariants({ variant }), className)} {...props}>
        {children}
      </div>
    )
  },
)

Container.displayName = "Container"

export { Container, containerVariants }

Usage examples

Here's how I usually use it:

// For blog posts and articles
<Container variant="narrowConstrainedPadded">
  <article>Your content here</article>
</Container>

// For full-width mobile, constrained desktop
<Container variant="fullMobileConstrainedPadded">
  <header>Your header content</header>
</Container>

The variant names follow Tailwind UI's conventions. They're long, but each name tells you what the variant does. After a few uses, you remember them.

The cn dependency

The component uses the cn utility from shadcn/ui to merge classes. If you already use shadcn/ui, you have it. If not, copy their cn implementation or combine clsx with tailwind-merge.

cn resolves conflicting Tailwind classes. If you pass px-2 in className, it replaces the variant's px-4 instead of both classes ending up on the element.

Why I built it

Tailwind UI has the container patterns, and shadcn/ui has the copy-it-into-your-project model. I hadn't found a component that combined them, so I made one.

If you use it, I'd like to hear how it works in your projects. The code lives in your project, so you can change it however you want. There's no package to update and no version to pin.