Skip to content
shadcn.io

shadcn.io is not affiliated with official shadcn/ui

Backgrounds

Pro

Paper Background for React

A sheet of paper behind your content: fine lit tooth, soft mottle, short curled fibres and a faint warm vignette. For React with Base UI, when a long read, a letter or a print-style page should feel like paper rather than a screen.

Framework

Paper puts a sheet of paper behind your content: a warm tint of your background with a faint vignette at the edges, like a sheet under a lamp, a soft mottle, a fine tooth lit from the top left so every bump throws a tiny shadow, and short, gently curled fibres that slowly fade a little and back. The tooth and the mottle are SVG noise tiles the browser draws once; the fibres are vector curves placed from a fixed seed when the component renders, drawn into two small tiles, so the sheet is the same on the server, on every load and at any size, and stays sharp at any zoom. The only motion is a slow fade of the fibre layer, which the browser composites without repainting; no script runs.

Use it for long reads, letters and print-style pages. For moving film speckle instead of a still sheet, use Grain; for printed dots, Halftone; for another material surface, Marble. Line art such as Topography sits well on a quiet sheet, Origami folds it into a Miura-ori, and Watercolor adds pigment washes.

Preview width

Installation

Part of Pro
Pro unlocks the code and one-line install of every Pro button and text effect, plus the MCP for your AI editor.
Have Pro already? Sign in to open it.Opens here as soon as you're Pro.

Usage

import { Paper } from "@/components/ui/paper";
<Paper className="min-h-[480px]">
  <h1>Field notes</h1>
</Paper>

Paper is a container: your content goes inside it, and the sheet sits behind that content. Give it a size with className; for a whole page, className="fixed inset-0 -z-10" with no children. Set the sheet's colour with paperColor, not a background class: the sheet covers the container's own background.

Demos

An Old Letter

ageSpots={6} scatters six soft, darker blotches over the sheet for a foxed, old-book look. Their places come from the same seed as the fibres, so they never move between renders.

Preview width

Kraft

paperColor and fiberColor take any CSS colour: a tan sheet with dark brown ink reads as kraft stock, and density={2} doubles the fibres for a heavier, recycled feel.

Preview width

Accessibility

The sheet is decoration: it's hidden from assistive tech and never takes the pointer, so everything you put inside works as usual. With reduced motion the fibres hold still at full strength, and in forced-colors mode the texture is hidden. Nothing flashes: the only motion is a fade by a fifth over 20 seconds. The tooth, the fibres and the vignette are faint enough that text in your foreground colour keeps its contrast (the darkest point of the vignette, in a corner, is a few percent darker than the sheet); with your own colours, check your text against the sheet.

FAQ

How do I make the paper more or less fibrous?

Use density: 0.5 for a clean letterpress sheet, 2 to 3 for heavy recycled stock, 0 for no fibres at all. The fibres keep their size on any screen; only their number changes.

Can I change the paper and ink colours?

Yes. paperColor and fiberColor take any CSS colour. Try kraft (#c8a67a with #3a2a18) or black stock with cream fibres. By default the sheet is a warm tint of your background and the ink is your foreground colour, so it follows light and dark mode.

What makes it look like real paper?

The tooth: fine noise is read as a height map and lit from the top left, so each bump has a lit side and a shadow, the way a sheet looks under a lamp. Together with the soft mottle, the short curled fibres and the vignette, it reads as a sheet, not a flat colour with noise on it.

What are age spots?

ageSpots={n} adds up to 12 soft, darker blotches for a foxed, old-book look. Their places are seeded, so they don't move between renders or on the server.

Does the texture change when the window resizes?

No. The fibres, the mottle and the tooth come from fixed tiles of different, co-prime sizes, so resizing only shows more or less of the same sheet, and the tiles never line up into a visible repeat.

Is it animated?

Barely. The fibre layer fades by a fifth and back over 20 seconds, on the compositor, the part of the browser that blends layers without repainting them. speed changes the pace; with reduced motion it's a still sheet.

Is it expensive?

No. The fibres are about a hundred short vector curves in two small tiles the browser draws once, and the mottle and the tooth are two noise tiles drawn once. No script runs in the browser.

API Reference

Paper

<div> data-slot="paper"

PropType
ageSpots?number

How many soft, darker blotches to scatter over the sheet, for a foxed, old-book look, from 0 to 12. Their places are seeded, so they never move.

Default
0
density?number

How many ink fibres, as a multiplier from 0 (a clean sheet) to 3 (heavy recycled stock). The fibres keep their size at any screen size.

Default
1
fiberColor?string

The ink of the fibres, the surface and the spots: any CSS colour. @default "var(--foreground)".

paperColor?string

The sheet's colour, any CSS colour. By default a warm tint of the background.

speed?number

How fast the fibres breathe: 2 is twice as fast, 0.5 half as fast.

Default
1
render?ReactElement | ((props: object, state: object) => ReactElement)

Replace the host element or compose it with another component.

Type
ReactElement | ((props: object, state: object) => ReactElement)
className?string

Merged with the part's classes.

CSS variableType
--paper-colorstring

The sheet's colour (a warm tint of --background).

--paper-fiber-colorstring

The ink of the fibres, surface and spots (var(--foreground)).

--paper-speedstring

How fast the fibres breathe, a multiplier (1).

All 100 backgrounds