[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.
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 viaFormField.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-writtenToggleGroupif you need multi-select. - Horizontal layout, field ordering, and custom input components require a manual form (the generator emits a vertical stack).