Skip to content
Facade UI
lib

Shared types

Types that every section uses: heading levels, image and link components you can replace, and call-to-action items.

Installation

Package manager
pnpm dlx shadcn@latest add https://facadeui.dev/r/types.json

Source

These are the files the CLI copies into your project.

lib/types.ts
/**
 * Shared contracts for every Facade UI section.
 *
 * Framework neutrality: nothing here imports from `next/*`. Sections that render
 * images or links inside data-driven lists take a *component type* rather than a
 * render callback, so `next/image` and `next/link` can be handed over directly
 * and still work inside React Server Components:
 *
 *   import Image from "next/image"
 *   <LogoCloud items={logos} image={Image} />
 *
 * One-off media uses a `media` ReactNode slot instead — no indirection needed.
 *
 * a11y: `HeadingLevel` exists so a section never hard-codes `<h2>`. Whoever
 * places the section knows the surrounding outline; the section does not.
 *
 * Dependencies: react (types only).
 */

import type { ElementType, ReactNode } from "react"

/** `<h1>` is reserved for the page; sections start at `<h2>`. */
export type HeadingLevel = 1 | 2 | 3 | 4 | 5 | 6

/** Maps a heading level to its tag. Use the `Heading` atom to render one. */
export type HeadingTag = `h${HeadingLevel}`

/**
 * The image props that sections pass to your image component. All are valid DOM
 * attributes, so the default `"img"` element works, and they also match `next/image`.
 */
export interface FacadeImageProps {
  src: string
  /** Required. Pass `""` only for decorative images. */
  alt: string
  width?: number
  height?: number
  className?: string
  sizes?: string
  loading?: "eager" | "lazy"
  decoding?: "async" | "auto" | "sync"
  fetchPriority?: "high" | "low" | "auto"
}

/** Drop-in for `next/image`, or the default `"img"`. */
export type ImageComponent = ElementType<FacadeImageProps>

/** The link props that sections pass to your link component. */
export interface FacadeLinkProps {
  href: string
  children?: ReactNode
  className?: string
  target?: string
  rel?: string
  "aria-current"?: "page" | "step" | "location" | "date" | "time" | "true" | "false"
  "aria-label"?: string
}

/** Drop-in for `next/link`, or the default `"a"`. */
export type LinkComponent = ElementType<FacadeLinkProps>

/**
 * Any icon component. Structural rather than nominal, so `lucide-react`,
 * `@heroicons/react`, a local SVG component, or anything else that accepts
 * SVG props can be passed without the registry depending on that library.
 *
 * One React Server Components caveat, which bites in exactly one place: an icon
 * component cannot be passed as a prop *across* a server-to-client boundary.
 * Most icon libraries, `lucide-react` included, do not mark their modules
 * `"use client"`, so the reference is a plain function that React refuses to
 * serialise. Static sections are server components and render icons in the same
 * tree, so they are unaffected. A `-motion` variant *is* a client component, so
 * whatever renders it must be a client component too. Adding `"use client"` to
 * the file that passes the icons is the whole fix.
 */
export type IconComponent = ElementType<{
  className?: string
  "aria-hidden"?: boolean | "true" | "false"
  strokeWidth?: number | string
  focusable?: boolean | "true" | "false"
}>

/** A call to action rendered by `CtaGroup` and by most sections' `actions` slot. */
export interface CtaItem {
  label: string
  href: string
  /** Defaults to `primary` for the first action and `outline` for the rest. */
  variant?: "primary" | "secondary" | "outline" | "ghost"
  /** Set for links that leave the site; adds `rel="noopener noreferrer"`. */
  external?: boolean
  "aria-label"?: string
}

/**
 * Lets you replace the elements a section uses for its list.
 *
 * By default a section renders `ul` and `li`. The `-motion` variant passes `Stagger` and
 * `StaggerItem` instead, so items animate one by one while the static file imports
 * nothing from `motion`.
 */
export interface ListSlotProps {
  /** Wraps the list. Defaults to `"ul"`. */
  listAs?: ElementType
  /** Wraps each item. Defaults to `"li"`. */
  itemAs?: ElementType
}

/**
 * The same as `ListSlotProps`, for sections made of stacked blocks instead of a list,
 * such as heroes and CTA bands.
 *
 * By default a section renders `div`s. The `-motion` variant passes `Stagger` and
 * `StaggerItem`, so the eyebrow, headline, text and buttons appear in sequence.
 */
export interface StackSlotProps {
  /** Wraps the content stack. Defaults to `"div"`. */
  stackAs?: ElementType
  /** Wraps each block in the stack. Defaults to `"div"`. */
  blockAs?: ElementType
}

/** Props shared by every section. */
export interface SectionBaseProps {
  /** Heading level for the section's own title. Defaults to `2`. */
  headingLevel?: HeadingLevel
  /**
   * Root element. Defaults to `"section"`. Use `"div"` when it is already inside a
   * `<section>`.
   */
  as?: "section" | "div" | "article" | "aside"
  className?: string
  /** Vertical spacing. Uses the `--facade-section-y*` tokens. */
  spacing?: "sm" | "md" | "lg" | "none"
  id?: string
}

Props

FacadeImageProps

The image props that sections pass to your image component. All are valid DOM attributes, so the default `"img"` element works, and they also match `next/image`.

Props for FacadeImageProps
PropTypeDefault
src*Requiredstring—
alt*RequiredRequired. Pass `""` only for decorative images.string—
widthnumber—
heightnumber—
classNamestring—
sizesstring—
loading"eager" | "lazy"—
decoding"async" | "auto" | "sync"—
fetchPriority"high" | "low" | "auto"—

FacadeLinkProps

The link props that sections pass to your link component.

Props for FacadeLinkProps
PropTypeDefault
href*Requiredstring—
childrenReactNode—
classNamestring—
targetstring—
relstring—
aria-current"page" | "step" | "location" | "date" | "time" | "true" | "false"—
aria-labelstring—

ListSlotProps

Lets you replace the elements a section uses for its list. By default a section renders `ul` and `li`. The `-motion` variant passes `Stagger` and `StaggerItem` instead, so items animate one by one while the static file imports nothing from `motion`.

Props for ListSlotProps
PropTypeDefault
listAsWraps the list. Defaults to `"ul"`.ElementType—
itemAsWraps each item. Defaults to `"li"`.ElementType—

StackSlotProps

The same as `ListSlotProps`, for sections made of stacked blocks instead of a list, such as heroes and CTA bands. By default a section renders `div`s. The `-motion` variant passes `Stagger` and `StaggerItem`, so the eyebrow, headline, text and buttons appear in sequence.

Props for StackSlotProps
PropTypeDefault
stackAsWraps the content stack. Defaults to `"div"`.ElementType—
blockAsWraps each block in the stack. Defaults to `"div"`.ElementType—

SectionBaseProps

Props shared by every section.

Props for SectionBaseProps
PropTypeDefault
headingLevelHeading level for the section's own title. Defaults to `2`.HeadingLevel—
asRoot element. Defaults to `"section"`. Use `"div"` when it is already inside a `<section>`."section" | "div" | "article" | "aside"—
classNamestring—
spacingVertical spacing. Uses the `--facade-section-y*` tokens."sm" | "md" | "lg" | "none"—
idstring—