Fluent settings
Declare a static settings(): Settings method instead of attributes, useful for repeaters, groups, or keeping configuration out of the constructor signature entirely:
use WearePixel\Inlay\Schema\Settings;
final class Faq extends Component implements InlayComponent
{
public function __construct(
public array $items = [],
) {}
public static function settings(): Settings
{
$itemSchema = Settings::make()
->text('question')->label('Question')->required()
->textarea('answer')->label('Answer');
return Settings::make()->repeater('items', $itemSchema)->label('FAQ items');
}
public function render(): View
{
return view('components.faq');
}
}
Settings::make() returns a builder. Each field-defining call (text(), textarea(), richtext(), number(), toggle(), select(), image(), link(), repeater(), group()) starts a new field; the modifier calls that follow it (label(), help(), required(), maxLength(), visibleWhen(), hiddenWhen(), options(), default()) apply to whichever field was most recently defined. Calling a modifier before any field has been defined, or declaring the same field name twice, both throw immediately - see the error table in Registering components.
Groups
group() works like repeater() but for a single nested object rather than a list:
public static function settings(): Settings
{
$addressSchema = Settings::make()
->text('street')->label('Street')
->text('city')->label('City');
return Settings::make()->group('address', $addressSchema)->label('Address');
}
Mixing attributes and fluent definitions
A single component can declare some settings via attributes and others via settings(). When both declare a field with the same name, the fluent declaration wins - the merge adopts every fluent field first, then adopts any attribute-declared field whose name wasn't already claimed. In practice: use attributes for the ordinary case, and drop into settings() only for the field that needs a repeater, a hand-tuned option list, or anything else attributes can't express.
Constructor defaults
The same rule from Attribute settings applies here: for a top-level setting, the constructor parameter's own default and nullability are the only source of truth. There is no schema-level .default() at the top level.
.default() on the fluent builder is meaningful only for a field nested inside a repeater or group item schema, which has no top-level constructor parameter to be authoritative instead:
$itemSchema = Settings::make()
->text('question')->label('Question')
->toggle('featured')->label('Featured')->default(false); // fine - nested, no constructor param