Rilaykit Logorilaykit ✨
Forms

Building Forms

How to use the fluent API to build form configurations.

Building a form configuration starts from your central rilay instance. By calling rilay.form(), you get a form builder that has access to all your pre-configured components and renderers.

Before proceeding, make sure you have created and configured a shared rilay instance as shown in the Your First Form guide.

The Form Building Process

1. Start with form()

Call .form() on your rilay instance to begin a new form definition. You should provide a unique ID for your form.

import { rilay } from '@/lib/rilay';

const loginForm = rilay.form('login');

2. Add Fields and Rows

The builder provides a powerful polymorphic .add() method that handles multiple use cases:

  • Single field: .add(fieldConfig) - Adds a field on its own row
  • Multiple fields: .add(field1, field2, field3) - Adds multiple fields on the same row (max 3)
  • Array syntax: .add([field1, field2], options) - Explicit row control with options
const registrationForm = rilay
  .form('registration')
  // Add multiple fields on the same row
  .add(
    {
      id: 'firstName',
      type: 'text', // This type must exist in your component registry
      props: { label: 'First Name' },
    },
    {
      id: 'lastName',
      type: 'text',
      props: { label: 'Last Name' },
    }
  )
  // Add a single field on its own row
  .add({
    id: 'email',
    type: 'email', // Assumes an 'email' type is registered
    props: { label: 'Email Address' },
  });

You can also use the array syntax for explicit control over row options:

const formWithOptions = rilay
  .form('styled-form')
  .add([
    { id: 'field1', type: 'text', props: { label: 'Field 1' } },
    { id: 'field2', type: 'text', props: { label: 'Field 2' } },
  ], { 
    spacing: 'loose', 
    alignment: 'center' 
  });

Auto-Generated IDs

One of the key improvements in the new API is automatic ID generation. If you don't provide an id field, one will be generated for you:

const quickForm = rilay
  .form('quick-form')
  .add(
    { type: 'text', props: { label: 'Name' } }, // Will get id: 'field-1'
    { type: 'email', props: { label: 'Email' } }, // Will get id: 'field-2'
    { type: 'text', props: { label: 'Phone' } } // Will get id: 'field-3'
  );

When to use .build()

You may have noticed we don't always call .build() in our examples. This is because components like <Form> and workflow steps are smart enough to build the configuration for you.

However, you should call .build() manually when you need the final, serializable FormConfiguration object. For instance:

  • To serialize the form and save it as JSON.
  • To pass the configuration to a custom function or a third-party tool.
  • For debugging purposes, to inspect the generated configuration.
const formConfig = rilay
  .form('my-form')
  .add({ id: 'field1', type: 'text' })
  .build(); // Manually build the config

console.log(formConfig.allFields);

Field Configuration

The fieldConfig object you pass to .add() has the following shape:

interface FieldConfig {
  id?: string; // Optional - auto-generated if not provided
  type: string; // The component type to render from your registry
  props?: Record<string, any>; // Props passed to your component renderer
}

Complete Example: Login Form

Let's tie everything together. Here is how you would build a complete login form configuration, assuming you have a configured rilay instance. This definition can be passed directly to the <Form> component.

import { rilay } from '@/lib/rilay';

export const loginForm = rilay
  .form('login-form')
  .add(
    {
      id: 'email',
      type: 'email',
      props: {
        label: 'Email Address',
        placeholder: 'Enter your email',
      },
    },
    {
      id: 'password',
      type: 'password',
      props: {
        label: 'Password',
        placeholder: 'Enter your password',
      },
    }
  );

This loginForm builder instance is now ready to be passed to your <Form> component.