Skip to content
weftGitHub

Shape

A ready-made loading placeholder for a list, card, table or block of text.

SourcePackage @weft-js/warpPlayground

Preview

tsx
import { Shape, Stack } from '@weft-js/warp'export function List() {  return (    <Stack><Shape kind="list" rows={3} /></Stack>  )}

Installation

npm install @weft-js/warp

Usage

tsx
import { Shape } from '@weft-js/warp'<Stack><Shape kind="list" rows={3} /></Stack>

Text

Use kind="text" with lines to draw a placeholder paragraph.

tsx
import { Shape, Stack } from '@weft-js/warp'export function Paragraph() {  return (    <Stack><Shape kind="text" lines={4} /></Stack>  )}

Card

media is the picture's ratio. Leave it out for none.

tsx
import { Shape, Stack } from '@weft-js/warp'export function Tile() {  return (    <Stack><Shape kind="card" media="16/9" lines={2} /></Stack>  )}

Table

Use kind="table" with rows and cols to draw a placeholder table.

tsx
import { Shape, Stack } from '@weft-js/warp'export function Table() {  return (    <Stack><Shape kind="table" rows={4} cols={4} /></Stack>  )}

Media

Use kind="media" with media to draw a placeholder image at a set ratio.

tsx
import { Shape, Stack } from '@weft-js/warp'export function Media() {  return (    <Stack><Shape kind="media" media="21/9" /></Stack>  )}

Waiting on data

Give it the content's counts, so the swap moves nothing.

tsx
import { Boundary, resource } from '@weft-js/core'import { Row, Shape, Stack, Text } from '@weft-js/warp'export function ShapeBoundary() {  return (    <Boundary pending={<Shape kind="text" lines={4} label="Loading owners" />}>      <Team />    </Boundary>  )}type Status = 'Live' | 'Building' | 'Failed' | 'Paused'const PROJECTS: { id: string; name: string; owner: string; region: string; status: Status; deploys: number; uptime: number; latency: number; cost: number; updated: string }[] = [  { id: 'storefront', name: 'storefront', owner: 'Priya Raman', region: 'eu-west', status: 'Live', deploys: 412, uptime: 99.98, latency: 84, cost: 1240, updated: '2026-10-31' },  { id: 'checkout', name: 'checkout', owner: 'Tomás Oliveira', region: 'eu-west', status: 'Live', deploys: 288, uptime: 99.95, latency: 112, cost: 980, updated: '2026-10-31' },  { id: 'search-api', name: 'search-api', owner: 'Lena Fischer', region: 'us-east', status: 'Building', deploys: 196, uptime: 99.91, latency: 146, cost: 1610, updated: '2026-10-30' },  { id: 'billing', name: 'billing', owner: 'Mara Lindqvist', region: 'us-east', status: 'Live', deploys: 154, uptime: 99.99, latency: 73, cost: 720, updated: '2026-10-29' },  { id: 'mobile-bff', name: 'mobile-bff', owner: 'Sam Okafor', region: 'ap-south', status: 'Failed', deploys: 233, uptime: 99.42, latency: 208, cost: 860, updated: '2026-10-29' },  { id: 'notifier', name: 'notifier', owner: 'Jun Tanaka', region: 'eu-north', status: 'Live', deploys: 97, uptime: 99.97, latency: 58, cost: 310, updated: '2026-10-27' },  { id: 'reports', name: 'reports', owner: 'Noor Haddad', region: 'us-west', status: 'Paused', deploys: 41, uptime: 99.8, latency: 321, cost: 190, updated: '2026-10-22' },  { id: 'edge-cache', name: 'edge-cache', owner: 'Jun Tanaka', region: 'global', status: 'Live', deploys: 318, uptime: 100, latency: 21, cost: 1430, updated: '2026-10-31' },  { id: 'auth', name: 'auth', owner: 'Mara Lindqvist', region: 'eu-west', status: 'Live', deploys: 176, uptime: 99.99, latency: 66, cost: 540, updated: '2026-10-28' },  { id: 'ingest', name: 'ingest', owner: 'Lena Fischer', region: 'us-east', status: 'Live', deploys: 264, uptime: 99.93, latency: 131, cost: 2120, updated: '2026-10-30' },]const later = <T,>(value: T) => new Promise<T>(done => { setTimeout(done, 1800, value) })function Team() {  const list = resource(() => later(PROJECTS.slice(0, 4)))  return (    <Stack gap="related">      {(list.value ?? []).map(p => <Row key={p.id} justify="between"><Text>{p.owner}</Text><Text subtle>{p.name}</Text></Row>)}    </Stack>  )}

When to use

Use it for

  • As the pending of a Boundary
  • Any loading state that looks like a list, card or table

Reach for something else

  • Skeleton, when the layout is unusual and you need to build it by hand

Playground

Kind
Rows3
import { Shape } from '@weft-js/warp'<Shape kind="list" />

Accessibility

  • It is a busy status named by label, and nothing inside it can be focused.

API Reference

PropTypeDefaultDescription
kindShapeKind—Which layout: text, list, card, media or table.
linesnumber—Text and card: lines of body text.
rowsnumber—List and table: how many rows.
colsnumber—Table: how many columns.
mediastring | number—Card and media: the ratio, like "16/9".
avatarbooleanfalseList: a circle at the start of each row.
labelstring'Loading'What screen readers hear.

RelatedSkeletonSpinner