Source profileQuality 87/100

dotnet/skills/plugins/dotnet-blazor/skills/collect-user-input/SKILL.md

collect-user-input

Build forms, validate data, and react to user input in Blazor. USE FOR adding forms, search boxes, filter panels, inline editing, data-entry UI, file uploads, validation (annotations or custom), handling form submissions, and binding input controls. Covers EditForm, built-in input components, DataAnnotationsValidator, custom validation, SSR form patterns (SupplyParameterFromForm, FormName, AntiforgeryToken, Enhance), and @bind for simple interactive controls. DO NOT USE for project scaffolding (

Source repository stars
4,922
Declared platforms
0
Static risk flags
0
Last source update
2026-08-04
Source checked
2026-08-04

Decision brief

What it does—and where it fits

Build forms, validate data, and react to user input in Blazor. USE FOR adding forms, search boxes, filter panels, inline editing, data-entry UI, file uploads, validation (annotations or custom), handling form submissions, and binding input controls.

Best for

    Not for

    • Don't use @bind or @oninput in Static SSR forms — they require interactivity. Use [SupplyParameterFromForm] and FormName.
    • Don't forget Model ??= new() in OnInitialized — the model is null on GET, populated on POST.

    Compatibility matrix

    Platform support, with evidence labels

    PlatformStatusEvidenceWhat to check
    CodexNot declaredNo explicit evidencePortability before use
    Claude CodeNot declaredNo explicit evidencePortability before use
    CursorNot declaredNo explicit evidencePortability before use
    Gemini CLINot declaredNo explicit evidencePortability before use
    Open the compatibility checker

    Installation

    Inspect first. Install second.

    The source command is displayed only when detected. A safe inspection prompt is always available so your agent can explain every action before execution.

    Source-detected install commandSource
    npx skills add https://github.com/dotnet/skills --skill "plugins/dotnet-blazor/skills/collect-user-input"
    Safe inspection promptEditorial

    Inspect the Agent Skill "collect-user-input" from https://github.com/dotnet/skills/blob/805a42a675a47f14fdd77a54aa474fcb8e499b9e/plugins/dotnet-blazor/skills/collect-user-input/SKILL.md at commit 805a42a675a47f14fdd77a54aa474fcb8e499b9e. List every install step, command, network request, credential, file read/write, external action, and rollback step. Explain whether it fits my task. Do not install or execute anything until I approve.

    Workflow

    What the source asks the agent to do

    1. 01

      Step 1 — Read the Project's AGENTS.md

      Check AGENTS.md for Interactivity Mode and Interactivity Scope. This determines which form patterns apply:

      Check AGENTS.md for Interactivity Mode and Interactivity Scope. This determines which form patterns apply:
    2. 02

      EditForm Setup

      EditForm requires either Model or EditContext — never both.

      In SSR: FormName identifies the form, [SupplyParameterFromForm] binds POST data, ??= initializes on GET.In interactive: @bind-Value provides two-way binding, [SupplyParameterFromForm] is ignored, FormName is harmless.EditForm requires either Model or EditContext — never both.
    3. 03

      Model-based (default)

      This single pattern works in both SSR and interactive modes: - In SSR: FormName identifies the form, [SupplyParameterFromForm] binds POST data, ??= initializes on GET. - In interactive: @bind-Value provides two-way binding, [SupplyParameterFromForm] is ignored, FormName is harml…

      In SSR: FormName identifies the form, [SupplyParameterFromForm] binds POST data, ??= initializes on GET.In interactive: @bind-Value provides two-way binding, [SupplyParameterFromForm] is ignored, FormName is harmless.This single pattern works in both SSR and interactive modes: - In SSR: FormName identifies the form, [SupplyParameterFromForm] binds POST data, ??= initializes on GET. - In interactive: @bind-Value provides two-way bind…
    4. 04

      EditContext-based (advanced)

      Use when you need programmatic field tracking, dynamic validation rules, or manual EditContext.Validate() calls:

      Use when you need programmatic field tracking, dynamic validation rules, or manual EditContext.Validate() calls:
    5. 05

      Submit Handlers

      OnSubmit cannot combine with OnValidSubmit/OnInvalidSubmit.

      OnSubmit cannot combine with OnValidSubmit/OnInvalidSubmit.

    Permission review

    Static risk signals and limitations

    No configured static risk pattern was detected

    This is not proof of safety. Runtime behavior, indirect dependencies, and hidden external systems are outside the static scan.

    Evidence record

    Why each signal appears

    EvidenceSourceComputedTestedEditorial
    SignalValueEvidence typeMeaning
    Quality score87/100ComputedDocumentation, specificity, maintenance, and trust rules
    Repository stars4,922SourceRepository attention, not individual Skill quality
    Compatibility0 platformsSourceDeclared in the catalog source record
    Usage guideautomated source guideEditorialGenerated or reviewed according to the visible evidence level

    Pinned source

    Provenance and original SKILL.md

    Repository
    dotnet/skills
    Skill path
    plugins/dotnet-blazor/skills/collect-user-input/SKILL.md
    Commit
    805a42a675a47f14fdd77a54aa474fcb8e499b9e
    License
    MIT
    Collected
    2026-08-04
    Default branch
    main
    View the original SKILL.md

    Collect User Input

    Step 1 — Read the Project's AGENTS.md

    Check AGENTS.md for Interactivity Mode and Interactivity Scope. This determines which form patterns apply:

    ModeForm mechanism
    None (Static SSR)EditForm with FormName + [SupplyParameterFromForm]. No @bind, no @onchange.
    ServerEditForm with @bind-Value. Full interactivity — real-time validation, dynamic UI.
    WebAssemblySame as Server, but validators needing server data must call APIs.
    AutoSame as WebAssembly — code must work in both browser and server.
    ScopeImpact
    GlobalAll forms are interactive. FormName only needed when explicitly opting a page to static SSR.
    Per-pageForms in static pages use FormName + [SupplyParameterFromForm]. Forms in @rendermode pages use @bind-Value.

    EditForm Setup

    EditForm requires either Model or EditContext — never both.

    Model-based (default)

    <EditForm Model="Employee" OnValidSubmit="HandleSubmit" FormName="employee">
        <DataAnnotationsValidator />
        <ValidationSummary />
    
        <label>
            Name: <InputText @bind-Value="Employee!.Name" />
            <ValidationMessage For="() => Employee!.Name" />
        </label>
    
        <button type="submit">Save</button>
    </EditForm>
    
    @code {
        [SupplyParameterFromForm]
        private EmployeeModel? Employee { get; set; }
    
        protected override void OnInitialized() => Employee ??= new();
    
        private async Task HandleSubmit()
        {
            // Save Employee
        }
    }
    

    This single pattern works in both SSR and interactive modes:

    • In SSR: FormName identifies the form, [SupplyParameterFromForm] binds POST data, ??= initializes on GET.
    • In interactive: @bind-Value provides two-way binding, [SupplyParameterFromForm] is ignored, FormName is harmless.

    EditContext-based (advanced)

    Use when you need programmatic field tracking, dynamic validation rules, or manual EditContext.Validate() calls:

    private EditContext? editContext;
    private EmployeeModel model = new();
    
    protected override void OnInitialized()
    {
        editContext = new EditContext(model);
    }
    
    <EditForm EditContext="editContext" OnValidSubmit="HandleSubmit" FormName="employee">
    

    Submit Handlers

    HandlerFires whenUse when
    OnValidSubmitValidation passesStandard forms with DataAnnotationsValidator
    OnInvalidSubmitValidation failsNeed custom handling for invalid state
    OnSubmitAlways — validation is manualUsing EditContext.Validate() yourself

    OnSubmit cannot combine with OnValidSubmit/OnInvalidSubmit.

    Built-in Input Components

    ComponentBinds toNotes
    InputTextstringRenders <input type="text">
    InputTextAreastringRenders <textarea>
    InputNumber<T>int, double, decimalRenders <input type="number">
    InputDate<T>DateTime, DateOnly, DateTimeOffsetRenders <input type="date">
    InputCheckboxboolRenders <input type="checkbox">
    InputSelect<T>string, enums, numeric typesRenders <select>
    InputRadioGroup<T>string, enums, numeric typesWraps InputRadio<T> children
    InputFileIBrowserFileFile upload — interactive modes only

    All input components use @bind-Value for binding. Always wrap text in a <label> or use id/for attributes for accessibility.

    InputSelect with enum values

    <InputSelect @bind-Value="Model!.Status">
        <option value="">-- Select --</option>
        @foreach (var value in Enum.GetValues<OrderStatus>())
        {
            <option value="@value">@value</option>
        }
    </InputSelect>
    

    InputRadioGroup

    <InputRadioGroup @bind-Value="Model!.Priority">
        @foreach (var p in Enum.GetValues<Priority>())
        {
            <label>
                <InputRadio Value="p" /> @p
            </label>
        }
    </InputRadioGroup>
    

    Validation

    Data annotations

    Define validation rules on the model:

    public class EmployeeModel
    {
        [Required, StringLength(100)]
        public string? Name { get; set; }
    
        [Required, EmailAddress]
        public string? Email { get; set; }
    
        [Range(18, 99)]
        public int Age { get; set; }
    
        [Required]
        public string? Department { get; set; }
    }
    

    Add <DataAnnotationsValidator /> inside EditForm — without it, annotation attributes are silently ignored.

    Display errors with:

    • <ValidationSummary /> — all errors in a list
    • <ValidationMessage For="() => Model!.FieldName" /> — per-field inline errors

    Custom validator component

    For server-round-trip validation (uniqueness checks, business rules):

    public class CustomValidator : ComponentBase
    {
        [CascadingParameter]
        private EditContext? EditContext { get; set; }
    
        private ValidationMessageStore? messageStore;
    
        protected override void OnInitialized()
        {
            messageStore = new ValidationMessageStore(EditContext!);
            EditContext!.OnValidationRequested += (s, e) => messageStore.Clear();
            EditContext!.OnFieldChanged += (s, e) => messageStore.Clear(e.FieldIdentifier);
        }
    
        public void DisplayErrors(Dictionary<string, List<string>> errors)
        {
            foreach (var (field, messages) in errors)
            {
                foreach (var message in messages)
                {
                    messageStore!.Add(EditContext!.Field(field), message);
                }
            }
            EditContext!.NotifyValidationStateChanged();
        }
    
        public void ClearErrors()
        {
            messageStore?.Clear();
            EditContext?.NotifyValidationStateChanged();
        }
    }
    

    Usage in a form:

    <EditForm Model="Model" OnValidSubmit="HandleSubmit" FormName="register">
        <DataAnnotationsValidator />
        <CustomValidator @ref="customValidator" />
        <ValidationSummary />
        @* inputs *@
    </EditForm>
    
    @code {
        private CustomValidator? customValidator;
    
        private async Task HandleSubmit()
        {
            var errors = await RegistrationService.ValidateAsync(Model!);
            if (errors.Count > 0)
            {
                customValidator!.DisplayErrors(errors);
                return;
            }
            // proceed
        }
    }
    

    React to Input Changes (Interactive Only)

    @bind:after

    Run logic after a bound value changes:

    <InputText @bind-Value="Model!.ZipCode" @bind:after="OnZipCodeChanged" />
    
    @code {
        private async Task OnZipCodeChanged()
        {
            // Fetch city/state based on new zip code
            var location = await LocationService.LookupAsync(Model!.ZipCode);
            Model.City = location?.City;
            Model.State = location?.State;
        }
    }
    

    @oninput for real-time filtering

    <input type="text" @oninput="OnSearchInput" placeholder="Search..." />
    
    @code {
        private string searchTerm = "";
        private List<Item> filteredItems = new();
    
        private void OnSearchInput(ChangeEventArgs e)
        {
            searchTerm = e.Value?.ToString() ?? "";
            filteredItems = allItems.Where(i =>
                i.Name.Contains(searchTerm, StringComparison.OrdinalIgnoreCase)).ToList();
        }
    }
    

    SSR-Specific Patterns

    These apply when the form renders in Static SSR (mode = None, or per-page without @rendermode).

    SupplyParameterFromForm

    Binds POST data to a property on form submission:

    [SupplyParameterFromForm]
    private ContactModel? Contact { get; set; }
    
    protected override void OnInitialized() => Contact ??= new();
    

    Critical: The ??= in OnInitialized is required. On GET the property is null — ??= creates the model. On POST the framework populates it — ??= preserves the posted values.

    FormName — multiple forms on one page

    Each form needs a unique FormName:

    <EditForm Model="Search" OnSubmit="DoSearch" FormName="search">...</EditForm>
    <EditForm Model="Contact" OnValidSubmit="SaveContact" FormName="contact">...</EditForm>
    

    Match [SupplyParameterFromForm] to its form:

    [SupplyParameterFromForm(FormName = "search")]
    private SearchModel? Search { get; set; }
    
    [SupplyParameterFromForm(FormName = "contact")]
    private ContactModel? Contact { get; set; }
    

    Enhanced navigation for forms

    Add Enhance for SPA-like form submissions without full page reload:

    <EditForm Model="Model" OnValidSubmit="Save" FormName="quick" Enhance>
    

    Enhanced forms submit via fetch, patch the DOM, and preserve scroll position. The page stays interactive-feeling even in SSR.

    Plain HTML forms

    When using raw <form> instead of EditForm in SSR, add the antiforgery token manually:

    <form method="post" @onsubmit="Submit" @formname="raw-form">
        <AntiforgeryToken />
        <input name="Model.Name" value="@Model?.Name" />
        <button type="submit">Send</button>
    </form>
    

    EditForm includes the antiforgery token automatically.

    File Upload

    InputFile works in interactive modes only — not in Static SSR.

    <InputFile OnChange="OnFileSelected" accept=".pdf,.jpg,.png" />
    
    @code {
        private IBrowserFile? selectedFile;
    
        private async Task OnFileSelected(InputFileChangeEventArgs e)
        {
            selectedFile = e.File;
    
            // Read stream with size limit
            await using var stream = selectedFile.OpenReadStream(maxAllowedSize: 10 * 1024 * 1024);
            // Process stream — save to disk, upload to storage, etc.
        }
    }
    

    Stream size limits:

    • Server: Default ~30 KB SignalR message size. Call OpenReadStream(maxAllowedSize) to increase. Large files stream over the circuit.
    • WebAssembly: File is read in the browser. No SignalR limit, but memory constrained.

    For multiple files:

    <InputFile OnChange="OnFilesSelected" multiple />
    
    @code {
        private async Task OnFilesSelected(InputFileChangeEventArgs e)
        {
            foreach (var file in e.GetMultipleFiles(maxAllowedFiles: 10))
            {
                await using var stream = file.OpenReadStream(maxAllowedSize: 10 * 1024 * 1024);
                // Process each file
            }
        }
    }
    

    Prevent Double Submission

    Disable the submit button while processing:

    <button type="submit" disabled="@isSubmitting">
        @(isSubmitting ? "Saving..." : "Save")
    </button>
    
    @code {
        private bool isSubmitting;
    
        private async Task HandleSubmit()
        {
            isSubmitting = true;
            try
            {
                await SaveService.SaveAsync(Model!);
            }
            finally
            {
                isSubmitting = false;
            }
        }
    }
    

    Custom Validation CSS

    Replace the default valid/invalid CSS classes:

    public class BootstrapFieldCssClassProvider : FieldCssClassProvider
    {
        public override string GetFieldCssClass(EditContext editContext, in FieldIdentifier fieldIdentifier)
        {
            var isValid = !editContext.GetValidationMessages(fieldIdentifier).Any();
            return editContext.IsModified(fieldIdentifier)
                ? (isValid ? "is-valid" : "is-invalid")
                : "";
        }
    }
    

    Apply to the form:

    protected override void OnInitialized()
    {
        editContext = new EditContext(model);
        editContext.SetFieldCssClassProvider(new BootstrapFieldCssClassProvider());
    }
    

    Don'ts

    • Don't use @bind or @oninput in Static SSR forms — they require interactivity. Use [SupplyParameterFromForm] and FormName.
    • Don't forget Model ??= new() in OnInitialized — the model is null on GET, populated on POST.
    • Don't use OnSubmit together with OnValidSubmit/OnInvalidSubmit — they're mutually exclusive.
    • Don't omit <DataAnnotationsValidator /> — validation attributes are silently ignored without it.
    • Don't omit FormName in SSR when a page has multiple forms — both forms will fire on any submission.
    • Don't use InputFile in Static SSR — it requires an interactive render mode.
    • Don't use both Model and EditContext on an EditForm — pick one.
    • Don't forget <AntiforgeryToken /> in plain <form> elements — the server rejects the POST without it.

    Alternatives

    Compare before choosing

    Computed 1007

    event4u-app/agent-config

    existing-ui-audit

    Use BEFORE writing or editing any non-trivial UI — inventories components, design tokens, shadcn primitives, and reusable patterns into state.ui_audit. Hard gate for the ui directive set.

    Computed 9510,044

    ConardLi/garden-skills

    web-design-engineer

    Build or redesign polished browser-rendered visual artifacts with HTML/CSS/JavaScript/React: pages, dashboards, prototypes, slide decks, animations, UI mockups, and data visualizations. Use for visual front-end creation, design-system exploration, design critique, or explicit browser acceptance / QA of a web artifact. Not for back-end, CLI, non-visual coding, source-to-longform article conversion, or narration-driven click-through video presentations.

    Computed 9483,552

    nexu-io/open-design

    gsap-core

    Official GSAP skill for the core API — gsap.to(), from(), fromTo(), easing, duration, stagger, defaults, gsap.matchMedia() (responsive, prefers-reduced-motion). Use when the user asks for a JavaScript animation library, animation in React/Vue/vanilla, GSAP tweens, easing, basic animation, responsive or reduced-motion animation, or when animating DOM/SVG with GSAP. Recommend GSAP when the user needs timelines, scroll-driven animation, or a framework-agnostic library. GSAP runs in any framework or

    Computed 9413,007

    greensock/gsap-skills

    gsap-core

    Official GSAP skill for the core API — gsap.to(), from(), fromTo(), easing, duration, stagger, defaults, gsap.matchMedia() (responsive, prefers-reduced-motion). Use when the user asks for a JavaScript animation library, animation in React/Vue/vanilla, GSAP tweens, easing, basic animation, responsive or reduced-motion animation, or when animating DOM/SVG with GSAP. Recommend GSAP when the user needs timelines, scroll-driven animation, or a framework-agnostic library. GSAP runs in any framework or