Skip to main content
Solar treats the props schema you pass to defineComponent() as a binding contract. Every time you call a component, Solar validates every prop before render ever runs. Pass the wrong type, omit a required prop, or use a value outside an enum — you get a ContractError immediately, with a structured JSON payload that tells you exactly what went wrong and how to fix it. There is no silent failure, no undefined leaking through to the render function.

Defining a prop schema

The props object in defineComponent() is a map of prop names to schema descriptors. Each descriptor can include the following fields:
type
string
required
The expected JavaScript type for the prop. Must be one of: string, number, boolean, function, object, array, any, slot. Solar uses this to run the type check at call time. Use any to skip the type check entirely, and slot for typed component composition — see the Slots page.
required
boolean
When true, Solar throws a ContractError if the prop is undefined or null. Omit this field (or set it to false) to make the prop optional.
default
any
A fallback value that Solar substitutes when the prop is not provided. Defaults are applied after the required check, so a prop with both required: true and default will still throw if the caller omits it.
enum
array
A list of allowed values for the prop. Solar throws a ContractError if the provided value is not in the list. Most useful with type: 'string' to restrict a prop to a known set of options.
accepts
string
Only valid when type is 'slot'. Names the component whose vnode output is the only accepted value for this slot. See the Slots page for the full slot API.

Full schema example

The component below uses every schema field. Study this as a reference before writing your own prop schemas.
const Form = defineComponent({
  name: 'Form',
  props: {
    title:    { type: 'string',   required: true },
    onSubmit: { type: 'function', required: true },
    mode:     { type: 'string',   enum: ['create', 'edit'], default: 'create' },
    count:    { type: 'number' },
    tags:     { type: 'array' },
    meta:     { type: 'object' },
    visible:  { type: 'boolean',  default: true },
  },
  render(props) { /* ... */ },
})

What happens when validation fails

When Solar detects a contract violation, it throws a ContractError. The error carries structured data — not just a string message — so you (and any AI agent in your toolchain) can parse and act on it without regex.
{
  "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"
}
The fields map directly to properties on the ContractError instance: component, prop, expected, received, fix, and the inherited message.
The fix field is intentionally prescriptive. It is written as a complete, actionable instruction so that an AI agent can read the structured error and re-generate the correct call without needing to parse the message string. When you catch a ContractError, prefer err.fix over err.message in any automated correction flow.

Handling contract errors

Use a try/catch block and check err.name to distinguish contract errors from other runtime errors. Call err.toJSON() to get the full structured payload.
try {
  Button({ label: 42, onClick: () => {} })
} catch (err) {
  if (err.name === 'ContractError') {
    console.log(err.toJSON()) // structured, parseable
  }
}
toJSON() returns the same shape as the JSON block above — safe to serialize and send to a logging service, a model API, or a developer console.

The any type

Use type: 'any' when you genuinely cannot know the shape of a prop at definition time — for example, a generic data-display component that accepts external API payloads.
props: {
  payload: { type: 'any', required: true },
}
Solar skips the type check entirely for any props and passes the value straight to render. There is no enum or additional validation for any — if you need structure-level validation, handle it inside render or in a wrapper component with a tighter schema.

Unknown props

If you pass a prop that is not declared in the schema, Solar does not throw. It logs a console.warn at the point of the call and then ignores the undeclared prop. The resolved props object passed to render will not contain it.
Button({ label: 'Save', onClick: fn, colour: 'red' })
// console.warn: Button: unknown prop "colour" passed but not declared in schema
This design keeps unknown props non-fatal while still surfacing the issue during development. If you see the warning, either add the prop to the schema or remove it from the call site.