React Component
TabbiedArtwork renders a Tabbied generative artwork into a normal, CSS-sizeable box — like an <img> — powered by css-doodle. Point it at any preset (or your own definition), size it with CSS, reseed it, and export it to PNG.
Install
The component ships in the tabbied package. React is an optional peer dependency — you only need it for the tabbied/react entry point.
npm install tabbiedBasic usage
Import the component and a preset, then render it inside a sized box. On the server and the first client paint it shows the artwork's background color (correct size, zero layout shift); the live pattern takes over once it mounts.
import { TabbiedArtwork } from 'tabbied/react';
import { radius } from 'tabbied/artworks';
export function Banner() {
return (
<TabbiedArtwork
artwork={radius}
seed="k9Pz"
fit="cover"
style={{ width: '100%', height: 320 }}
/>
);
}Fit modes
The fit prop controls how the artwork relates to its box:
grid(default) adapts the cell grid to the measured container.stretchkeeps the authored grid and stretches it to fill.cover/containscale a fixed-resolution render (preserving fixed-px effects) to fill or letterbox the box.fixedrenders at explicitwidth/heightprops.
Each artwork also declares a sensible default, so fit is optional.
// grid (default): the cell grid adapts to the container size
<TabbiedArtwork artwork={radius} fit="grid" />
// cover: a fixed-resolution render is scaled to fill the box
<TabbiedArtwork artwork={radius} fit="cover" />
// contain: letterboxed at the artwork's authored ratio
<TabbiedArtwork artwork={symmetry} fit="contain" />
// stretch keeps the authored grid; fixed renders at width/height propsCustom palette
Pass palette to recolor a design — the background color (color0) comes first. Unspecified slots fall back to the preset's palette.
<TabbiedArtwork
artwork={radius}
seed="k9Pz"
// color0 (the background) comes first
palette={['#0b132b', '#5bc0be', '#6fffe9', '#ff6b6b']}
fit="cover"
style={{ width: '100%', height: 280 }}
/>Options
Every preset exposes adjustable options — the same controls the Tabbied editor shows. Pass them keyed by option id; anything you omit uses the authored default.
// Option ids come from the preset (the same controls the editor shows).
// Radius takes a grid size, a shape frequency, and a shadow toggle.
<TabbiedArtwork
artwork={radius}
seed="k9Pz"
options={{ grid: '4x6', shadow: true }}
fit="cover"
style={{ width: '100%', height: 280 }}
/>Reseed & export
Grab a ref to the component's handle to drive it imperatively: redraw() re-randomizes the seed (designs with CSS transitions morph between variations), and exportImage() saves a PNG. Try it:
import { useRef } from 'react';
import { TabbiedArtwork, type TabbiedArtworkHandle } from 'tabbied/react';
import { radius } from 'tabbied/artworks';
export function Reseedable() {
const ref = useRef<TabbiedArtworkHandle>(null);
return (
<>
<TabbiedArtwork ref={ref} artwork={radius} fit="cover" />
<button onClick={() => ref.current?.redraw()}>Redraw</button>
<button onClick={() => ref.current?.exportImage()}>Export PNG</button>
</>
);
}Ambient animation
Set redrawIntervalto reseed on a timer — the gallery's shimmer. It pauses while the tab is hidden (or when paused is set) and is skipped entirely under prefers-reduced-motion.
// Reseed on a timer (the gallery's shimmer). Paused while the tab is
// hidden, and skipped entirely under prefers-reduced-motion.
<TabbiedArtwork
artwork={quilt}
fit="cover"
redrawInterval={2000}
style={{ width: '100%', height: 280 }}
/>Props
| Prop | Type | Description |
|---|---|---|
| artwork | ArtworkDefinition | The artwork to render. Import a preset from tabbied/artworks or pass your own definition. Required. |
| seed | string | Pattern seed. Omit for a random seed per mount; reseed via the handle. |
| palette | string[] | Active colors, background (color0) first. Defaults to the preset palette. |
| options | Record<string, OptionValue> | Option values keyed by option id; unset options use authored defaults. |
| fit | 'grid' | 'stretch' | 'cover' | 'contain' | 'fixed' | How the artwork fills its box. Defaults per artwork. |
| cellSize | number | fit="grid" — target cell size in px (default 36). |
| density | 0 | 1 | 2 | 3 | 4 | fit="grid" — authored density level, an alternative to cellSize. |
| width / height | number | fit="fixed" — canvas size in px. |
| coverRender | { width, height, cropTop? } | cover/contain render resolution override. |
| redrawInterval | number | Re-randomize the seed every N ms (uncontrolled seed only). |
| paused | boolean | Pause redrawInterval ticks without resetting the timer. |
| decorative | boolean | true (default) renders an aria-hidden image; false exposes role="img" with ariaLabel. |
| onReady | () => void | Called once the first pattern render is committed. |
| className / style | string / CSSProperties | Applied to the wrapper box. |
See the inline JSDoc on TabbiedArtworkProps for the full list.
Handle (ref)
A ref exposes a TabbiedArtworkHandle:
| Member | Description |
|---|---|
redraw(seed?: string) | Re-randomize (or set) the seed, animating designs with CSS transitions. |
exportImage(options?) | PNG export via css-doodle. Returns a promise. |
element | The raw <css-doodle> element, for power users. |
Importing presets
artwork takes an ArtworkDefinition. Each preset is a side-effect-free named export, so importing only the ones you render keeps unused designs out of your bundle.
// Import only what you render — bundlers ship just those presets.
import { radius, symmetry } from 'tabbied/artworks';
// Building a gallery? The full record pulls in every design.
import { artworks } from 'tabbied/artworks';Server components
TabbiedArtwork is a client component (it registers a browser custom element on import). In the Next.js App Router, render it from a client boundary, or rely on its built-in measurable placeholder — it renders a correctly-sized box on the server and hydrates without a mismatch.
Not using React?
The same engine is available framework-free as createArtwork from tabbied.
import { createArtwork } from 'tabbied';
import { radius } from 'tabbied/artworks';
const controller = createArtwork(document.querySelector('#stage'), {
artwork: radius,
seed: 'k9Pz',
});
controller.redraw(); // re-randomize the seed
await controller.exportImage();
controller.destroy();Browse the source, the full prop docs, and all 84 presets on GitHub.