Component props in BarefootJS are typed with TypeScript interfaces. The compiler preserves type information through compilation, and the adapter uses it to generate type-safe server templates.

Props in client components are reactive — how you access them matters. See Props Reactivity for the full explanation. This page focuses on typing patterns.

#Defining Props

Define props as an interface or type alias and annotate the function parameter:

interface GreetingProps {
  name: string
  greeting?: string
}

export function Greeting(props: GreetingProps) {
  return <h1>{props.greeting ?? 'Hello'}, {props.name}</h1>
}

#Default Values

Use nullish coalescing (??) on the props object:

function Button(props: { variant?: 'default' | 'primary'; children?: Child }) {
  const variant = props.variant ?? 'default'
  return <button className={variant}>{props.children}</button>
}

For props that are only used as an initial value for a signal, default parameter syntax is also fine. Note that the compiler emits warning BF043 for destructuring, so add @bf-ignore to signal intent:

// @bf-ignore props-destructuring
function Counter({ initial = 0 }: { initial?: number }) {
  const [count, setCount] = createSignal(initial)
  return <button onClick={() => setCount(n => n + 1)}>{count()}</button>
}

#Extending HTML Attributes

For components that wrap native elements, extend the corresponding HTML attribute type:

import type { ButtonHTMLAttributes } from '@barefootjs/jsx'

interface ButtonProps extends ButtonHTMLAttributes {
  variant?: 'default' | 'primary' | 'destructive'
  size?: 'sm' | 'md' | 'lg'
}

function Button(props: ButtonProps) {
  const variant = props.variant ?? 'default'
  const size = props.size ?? 'md'
  const classes = `btn btn-${variant} btn-${size} ${props.className ?? ''}`

  return <button className={classes} {...props}>{props.children}</button>
}

This lets callers pass any standard button attribute (type, disabled, aria-label, etc.) alongside custom props.

#Rest Spreading

Use rest syntax to forward unknown props to the underlying element. Since rest spreading captures values once, use it for server components or for attributes that don't need reactive updates:

function Card(props: { title: string; children?: Child } & HTMLAttributes) {
  return (
    <div className={props.className}>
      <h2>{props.title}</h2>
      {props.children}
    </div>
  )
}
<Card title="Dashboard" className="shadow-lg" data-testid="dashboard-card">
  <p>Content</p>
</Card>

#Type Preservation

The compiler captures TypeScript type information for props and carries it through the IR. Each adapter uses this information to generate type-safe server output. For details on how types are mapped to specific backends, see Adapters.