# LumeoForm Source Generator

Source: https://lumeo.nativ.sh/docs/lumeo-form

# \[LumeoForm\] Source Generator

Hand-writing a Blazor form for every POCO — picking the right input per property, wiring `@bind-Value`, repeating `<FormField Label="…" Required>` — is pure boilerplate. The `[LumeoForm]` Roslyn source generator does it for you at compile time.

Annotate any class with `[LumeoForm]` and make it `partial`. The generator emits a `static RenderForm(model, onValidSubmit)` method that returns a ready-to-use `RenderFragment`.

## 1\. Annotate a POCO

using System.ComponentModel.DataAnnotations;
using Lumeo;

\[LumeoForm(Title = "Contact us", SubmitLabel = "Send message")\]
public partial class ContactFormModel
{
    \[Required, StringLength(80, MinimumLength = 2)\]
    \[Display(Name = "Full name", Description = "As it should appear on the invoice.")\]
    public string Name { get; set; } = "";

    \[Required, DataType(DataType.EmailAddress)\]
    public string Email { get; set; } = "";

    \[DataType(DataType.Password), StringLength(64, MinimumLength = 8)\]
    public string Password { get; set; } = "";

    \[Range(1, 120)\]
    public int Age { get; set; } = 25;

    public bool Subscribe { get; set; }
    public ContactPreference Preference { get; set; }
}

public enum ContactPreference { Email, Phone, SmsMessage }

## 2\. Render it

Use the generated `RenderForm` method as a `RenderFragment` — either assign it to a field and invoke it as `@_formFragment` or render it directly.

@page "/contact"
@using MyApp.Models

<h1>Contact</h1>

@ContactFormModel.RenderForm(\_model, EventCallback.Factory.Create<ContactFormModel>(this, HandleSubmit))

@code {
    private readonly ContactFormModel \_model = new();

    private Task HandleSubmit(ContactFormModel m)
    {
        // \_model is validated — persist it.
        return Task.CompletedTask;
    }
}

## 3\. Live demo

The form below was generated from `ContactFormModel` at build time — no hand-written Razor.

### Contact us

Full name\*

As it should appear on the invoice.

Email\*

Password

Age\*

Subscribe to newsletter\*

Preferred contact\*

Email Phone Sms Message

Send message

## Supported property types

Property type

Generated component

Notes

`string`

`<Input />`

Also honours `[DataType(DataType.EmailAddress)]` → `type="email"`.

`string` + `[DataType(Password)]`

`<PasswordInput />`

Masked entry, strength meter.

`int`, `long`, `double`, `decimal`, …

`<NumberInput />`

Coerces to/from `double?` internally.

`bool`

`<Checkbox />`

Bound via `Checked` / `CheckedChanged`.

`DateTime`, `DateTimeOffset`

`<DatePicker />`

Uses the picker's `DateTimeValue` binding.

`DateOnly`

`<DatePicker />`

Default `DateOnly?` binding.

`enum`

`<Select />` + one `<SelectItem />` per member

Labels PascalCase-split to "Sms Message".

## Validation attributes

The generator wires the built-in `DataAnnotationsFormValidator` into every generated form, so any standard `System.ComponentModel.DataAnnotations` attribute runs at submit time without extra code:

-   `[Required]` — also marks the field visually via `FormField.Required`.
-   `[StringLength(max, MinimumLength = n)]`
-   `[Range(min, max)]`
-   `[DataType(DataType.EmailAddress)]` / `[DataType(DataType.Password)]` — influences input choice.
-   `[Display(Name = "…", Description = "…")]` — `Name` → field label, `Description` → help text.

## Attribute options

Property

Type

Default

Description

`Title`

`string?`

`null`

Optional heading rendered above the form.

`IncludeSubmitButton`

`bool`

`true`

Whether to append a submit button at the end.

`SubmitLabel`

`string`

`"Submit"`

Label on the generated submit button.

## Known limitations

-   Target classes must be marked `partial`.
-   Only public read/write properties with a public setter are rendered.
-   Collections, nested records, and custom value types are skipped — use a hand-written form for those.
-   Flag enums render as a single `Select`; use a hand-written `ToggleGroup` if you need multi-select.
-   Horizontal layout, field ordering, and custom input components require a manual form (the generator emits a vertical stack).
