Build type-safe, dynamic forms in Blazor with ease β¨
Get Started β’ Live Demo β’ Documentation β’ Examples β’ Contributing
Experience FormCraft in action! Visit our interactive demo to see:
- π― Various form layouts and configurations
- π Dynamic field dependencies
- β¨ Custom field renderers
- π€ File upload capabilities
- π¨ Real-time form generation
FormCraft v2.5.0 introduces powerful attribute-based form generation and more:
- Zero-configuration forms - Generate complete forms from model attributes
- Rich attribute library - TextField, EmailField, NumberField, DateField, SelectField, CheckboxField, TextArea
- Automatic validation - Integrates with DataAnnotations attributes
- One-line setup - Just call
.AddFieldsFromAttributes()
- Field-level encryption for sensitive data protection
- CSRF protection with built-in anti-forgery tokens
- Rate limiting to prevent form spam
- Audit logging to track all form interactions
- Separate UI framework packages - Use only what you need
- FormCraft.ForMudBlazor - MudBlazor implementation package
- Improved extensibility - Easier to add custom UI frameworks
- Enhanced performance with optimized rendering
- Better type safety with improved generic constraints
- Comprehensive documentation with live examples
- 550+ unit tests ensuring reliability
FormCraft revolutionizes form building in Blazor applications by providing a fluent, type-safe API that makes complex forms simple. Say goodbye to repetitive form markup and hello to elegant, maintainable code.
- π Type-Safe - Full IntelliSense support with compile-time validation
- π― Fluent API - Intuitive method chaining for readable form configuration
- π·οΈ Attribute-Based Forms - Generate forms from model attributes with zero configuration
- π¨ MudBlazor Integration - Beautiful Material Design components out of the box
- π Dynamic Forms - Create forms that adapt based on user input
- β Advanced Validation - Built-in, custom, and async validators
- π Field Dependencies - Link fields together with reactive updates
- π Flexible Layouts - Multiple layout options to fit your design
- π High Performance - Optimized rendering with minimal overhead
- π§ͺ Fully Tested - 550+ unit tests ensuring reliability
dotnet add package FormCraft
dotnet add package FormCraft.ForMudBlazor
Note: FormCraft.ForMudBlazor includes FormCraft as a dependency, so you only need to install the MudBlazor package if you're using MudBlazor components.
// Program.cs
builder.Services.AddFormCraft();
public class UserRegistration
{
public string FirstName { get; set; }
public string LastName { get; set; }
public string Email { get; set; }
public int Age { get; set; }
public string Country { get; set; }
public bool AcceptTerms { get; set; }
}
@page "/register"
@using FormCraft
@using FormCraft.ForMudBlazor
<h3>User Registration</h3>
<FormCraftComponent TModel="UserRegistration"
Model="@model"
Configuration="@formConfig"
OnValidSubmit="@HandleSubmit"
ShowSubmitButton="true" />
@code {
private UserRegistration model = new();
private IFormConfiguration<UserRegistration> formConfig;
protected override void OnInitialized()
{
formConfig = FormBuilder<UserRegistration>.Create()
.AddRequiredTextField(x => x.FirstName, "First Name")
.AddRequiredTextField(x => x.LastName, "Last Name")
.AddEmailField(x => x.Email)
.AddNumericField(x => x.Age, "Age", min: 18, max: 120)
.AddSelectField(x => x.Country, "Country", GetCountries())
.AddCheckboxField(x => x.AcceptTerms, "I accept the terms and conditions")
.IsRequired("You must accept the terms")
.Build();
}
private async Task HandleSubmit(UserRegistration model)
{
// Handle form submission
await UserService.RegisterAsync(model);
}
private List<SelectOption<string>> GetCountries() => new()
{
new("us", "United States"),
new("uk", "United Kingdom"),
new("ca", "Canada"),
new("au", "Australia")
};
}
Define your forms directly on your model with attributes - no configuration code needed!
public class UserRegistration
{
[TextField("First Name", "Enter your first name")]
[Required(ErrorMessage = "First name is required")]
[MinLength(2)]
public string FirstName { get; set; } = string.Empty;
[TextField("Last Name", "Enter your last name")]
[Required(ErrorMessage = "Last name is required")]
public string LastName { get; set; } = string.Empty;
[EmailField("Email Address")]
[Required]
public string Email { get; set; } = string.Empty;
[NumberField("Age", "Your age")]
[Range(18, 120, ErrorMessage = "Age must be between 18 and 120")]
public int Age { get; set; }
[DateField("Date of Birth")]
public DateTime BirthDate { get; set; }
[SelectField("Country", "United States", "Canada", "United Kingdom", "Australia")]
public string Country { get; set; } = string.Empty;
[TextArea("Bio", "Tell us about yourself")]
[MaxLength(500)]
public string Bio { get; set; } = string.Empty;
[CheckboxField("Newsletter", "Subscribe to our newsletter")]
public bool SubscribeToNewsletter { get; set; }
}
var formConfig = FormBuilder<UserRegistration>.Create()
.AddFieldsFromAttributes() // That's it! π
.Build();
[TextField]
- Standard text input[EmailField]
- Email input with validation[NumberField]
- Numeric input with min/max support[DateField]
- Date picker with constraints[SelectField]
- Dropdown with predefined options[CheckboxField]
- Boolean checkbox[TextArea]
- Multiline text input
All attributes work seamlessly with standard DataAnnotations validators like [Required]
, [MinLength]
, [MaxLength]
, [Range]
, and more!
Fluent API | Attribute-Based |
---|---|
var config = FormBuilder<User>.Create()
.AddField(x => x.Name, field => field
.WithLabel("Full Name")
.WithPlaceholder("Enter name")
.Required("Name is required")
.WithMinLength(2))
.AddField(x => x.Email, field => field
.WithLabel("Email")
.WithInputType("email")
.Required())
.Build(); |
public class User
{
[TextField("Full Name", "Enter name")]
[Required(ErrorMessage = "Name is required")]
[MinLength(2)]
public string Name { get; set; }
[EmailField("Email")]
[Required]
public string Email { get; set; }
}
// One line to generate!
var config = FormBuilder<User>.Create()
.AddFieldsFromAttributes()
.Build(); |
Create forms where fields react to each other:
var formConfig = FormBuilder<OrderForm>.Create()
.AddSelectField(x => x.ProductType, "Product Type", productOptions)
.AddSelectField(x => x.ProductModel, "Model",
dependsOn: x => x.ProductType,
optionsProvider: (productType) => GetModelsForType(productType))
.AddNumericField(x => x.Quantity, "Quantity", min: 1)
.AddField(x => x.TotalPrice, "Total Price")
.IsReadOnly()
.DependsOn(x => x.ProductModel, x => x.Quantity)
.WithValueProvider((model, _) => CalculatePrice(model))
.Build();
Add complex validation logic with ease:
.AddField(x => x.Username)
.WithValidator(new CustomValidator<User, string>(
username => !forbiddenUsernames.Contains(username.ToLower()),
"This username is not available"))
.WithAsyncValidator(async (username, services) =>
{
var userService = services.GetRequiredService<IUserService>();
return await userService.IsUsernameAvailableAsync(username);
}, "Username is already taken")
Choose the layout that fits your design:
// Vertical Layout (default)
.WithLayout(FormLayout.Vertical)
// Horizontal Layout
.WithLayout(FormLayout.Horizontal)
// Grid Layout
.WithLayout(FormLayout.Grid, columns: 2)
// Inline Layout
.WithLayout(FormLayout.Inline)
// Password field with confirmation
.AddPasswordField(x => x.Password, "Password")
.WithHelpText("Must be at least 8 characters")
.AddPasswordField(x => x.ConfirmPassword, "Confirm Password")
.MustMatch(x => x.Password, "Passwords do not match")
// Date picker with constraints
.AddDateField(x => x.BirthDate, "Date of Birth")
.WithMaxDate(DateTime.Today.AddYears(-18))
.WithHelpText("Must be 18 or older")
// Multi-line text with character limit
.AddTextAreaField(x => x.Description, "Description", rows: 5)
.WithMaxLength(500)
.WithHelpText("Maximum 500 characters")
// File upload
.AddFileUploadField(x => x.Resume, "Upload Resume",
acceptedFileTypes: new[] { ".pdf", ".doc", ".docx" },
maxFileSize: 5 * 1024 * 1024) // 5MB
// Multiple file upload
.AddMultipleFileUploadField(x => x.Documents, "Upload Documents",
maxFiles: 3,
acceptedFileTypes: new[] { ".pdf", ".jpg", ".png" },
maxFileSize: 10 * 1024 * 1024) // 10MB per file
Show/hide fields based on conditions:
.AddField(x => x.CompanyName)
.VisibleWhen(model => model.UserType == UserType.Business)
.AddField(x => x.TaxId)
.RequiredWhen(model => model.Country == "US")
Organize related fields into groups with customizable layouts:
var formConfig = FormBuilder<UserModel>
.Create()
.AddFieldGroup(group => group
.WithGroupName("Personal Information")
.WithColumns(2) // Two-column layout
.ShowInCard(2) // Show in card with elevation 2
.AddField(x => x.FirstName, field => field
.WithLabel("First Name")
.Required())
.AddField(x => x.LastName, field => field
.WithLabel("Last Name")
.Required())
.AddField(x => x.DateOfBirth))
.AddFieldGroup(group => group
.WithGroupName("Contact Information")
.WithColumns(3) // Three-column layout
.ShowInCard() // Default elevation 1
.AddField(x => x.Email)
.AddField(x => x.Phone)
.AddField(x => x.Address))
.Build();
Protect your forms with built-in security features:
var formConfig = FormBuilder<SecureForm>.Create()
.WithSecurity(security => security
.EncryptField(x => x.SSN) // Encrypt sensitive fields
.EncryptField(x => x.CreditCard)
.EnableCsrfProtection() // Enable anti-forgery tokens
.WithRateLimit(5, TimeSpan.FromMinutes(1)) // Max 5 submissions per minute
.EnableAuditLogging()) // Log all form interactions
.AddField(x => x.SSN, field => field
.WithLabel("Social Security Number")
.WithMask("000-00-0000"))
.AddField(x => x.CreditCard, field => field
.WithLabel("Credit Card")
.WithMask("0000 0000 0000 0000"))
.Build();
Create specialized input controls for specific field types:
// Create a custom renderer
public class ColorPickerRenderer : CustomFieldRendererBase<string>
{
public override RenderFragment Render(IFieldRenderContext context)
{
return builder =>
{
var value = GetValue(context) ?? "#000000";
builder.OpenElement(0, "input");
builder.AddAttribute(1, "type", "color");
builder.AddAttribute(2, "value", value);
builder.AddAttribute(3, "onchange", EventCallback.Factory.CreateBinder<string>(
this, async (newValue) => await SetValue(context, newValue), value));
builder.CloseElement();
};
}
}
// Use in your form configuration
.AddField(x => x.Color, field => field
.WithLabel("Product Color")
.WithCustomRenderer<ProductModel, string, ColorPickerRenderer>()
.WithHelpText("Select the primary color"))
// Register custom renderers (optional for DI)
services.AddScoped<ColorPickerRenderer>();
services.AddScoped<RatingRenderer>();
Built-in example renderers:
- ColorPickerRenderer - Visual color selection with hex input
- RatingRenderer - Star-based rating control using MudBlazor
FormCraft is designed for optimal performance:
- β‘ Minimal re-renders using field-level change detection
- π― Targeted validation execution
- π Efficient dependency tracking
- π¦ Small bundle size (~50KB gzipped)
FormCraft is extensively tested with over 400 unit tests covering:
- β All field types and renderers
- β Validation scenarios
- β Field dependencies
- β Edge cases and error handling
- β Integration scenarios
We love contributions! Please see our Contributing Guide for details.
# Clone the repository
git clone https://github.com/phmatray/FormCraft.git
# Build the project
dotnet build
# Run tests
dotnet test
# Create a local NuGet package
./pack-local.sh # or pack-local.ps1 on Windows
π Complete Documentation - Interactive docs with live examples
- File upload field type
- Security features (encryption, CSRF, rate limiting, audit logging)
- Modular UI framework architecture
- Import/Export forms as JSON
- Rich text editor field
- Wizard/stepper forms
- Drag-and-drop form builder UI
- Form templates library
- Localization support
- More layout options
- Integration with popular CSS frameworks
- Form state persistence
- Discussions: GitHub Discussions
- Issues: GitHub Issues
- Twitter: @phmatray
FormCraft is licensed under the MIT License.
- MudBlazor for the amazing component library
- FluentValidation for validation inspiration
- The Blazor community for feedback and support
If you find FormCraft useful, please consider giving it a β on GitHub!
Made with β€οΈ by phmatray