Skip to main content

defineComponent

The core primitive in Solar. Every component is defined through a schema that declares its shape explicitly — props are typed and validated at runtime before the component renders.
import { defineComponent, createElement, registry } from 'solarbuild'

const Button = defineComponent({
  name: 'Button',
  props: {
    label: { type: 'string', required: true },
    onClick: { type: 'function', required: true },
    variant: { type: 'string', enum: ['primary', 'secondary'], default: 'primary' },
  },
  render({ label, onClick, variant }) {
    return createElement('button', { class: variant, onclick: onClick }, label)
  },
})

registry.register(Button)
export default Button

Config

name
string
required
The component name. Used in error messages, the registry, and slot validation. Must be unique.
props
object
Schema defining the component’s accepted props. Each key is a prop name, each value is a prop descriptor.
render
function
required
Receives the resolved props (defaults applied, validated) and returns a vnode.

Prop descriptor

Each entry in props accepts the following fields:
FieldTypeDescription
typestringOne of string, number, boolean, function, object, array, any, slot
requiredbooleanThrows if the prop is missing or null
defaultanyValue used when the prop is not passed
enumarrayRestricts the value to a list of allowed strings
acceptsstringFor slot type only — restricts to a specific component name

Prop types

import { Types } from 'solarbuild'

// Types is a convenience object — the string values work directly too
Types.string    // 'string'
Types.number    // 'number'
Types.boolean   // 'boolean'
Types.function  // 'function'
Types.object    // 'object'
Types.array     // 'array'
Types.any       // 'any'
Types.slot      // 'slot'

Contract errors

If a prop contract is violated, Solar throws a structured ContractError immediately — before the component renders:
{
  "error": "ContractError",
  "component": "Button",
  "prop": "label",
  "expected": "string",
  "received": "number",
  "fix": "Pass a string value for \"label\"",
  "message": "Button: prop \"label\": expected string, got number"
}
Use e.toJSON() to get the structured object, or e.message for the human-readable string.

Slots

Slots are typed component composition. A slot prop expects a vnode produced by a specific component:
const Card = defineComponent({
  name: 'Card',
  props: {
    title: { type: 'string', required: true },
    action: { type: 'slot', accepts: 'Button' }, // must be a Button vnode
  },
  render({ title, action }) {
    return createElement('div', { class: 'card' },
      createElement('h2', {}, title),
      action,
    )
  },
})
Passing a plain createElement vnode instead of a Button vnode throws a ContractError.

Return value

defineComponent returns a component function. Call it with props to get a vnode:
Button({ label: 'Save', onClick: handleSave, variant: 'primary' })
The returned vnode has a _source property set to the component name — used by the slot system for type checking.