Creating Web Applications

A project is rarely made up only of a backend. With Lino, you can also create web applications to consume the generated APIs, providing interfaces for end users or internal teams.


The framework currently provides native support for Blazor Web App (rendering mode interactive auto). However, the architecture was designed to allow multiple frontends to be added to the same solution, serving different scenarios and audiences.

Frontend architecture with Blazor Web App

Web applications generated by Lino connect users to backend use cases through a real interface, not only through calls made from API tools. The current native frontend type is Blazor Web App, prepared for administrative applications, public portals, backoffices, partner areas, and domain-specific interfaces.

lino web-app new --name <WebAppName>

The command must be run inside a Lino project. It asks for the Web App name, uses the Blazor Web App template, confirms the selected data, creates the required files, and runs the commands that connect the frontend to the solution. The canonical form is web-app. The aliases webapp and web exist for productivity, but documentation and examples should prefer lino web-app new to keep consistency.

What is generated

A Blazor Web App generated by Lino is not just a loose folder of screens. It becomes part of the same .NET solution and follows the modular organization used by the backend, separating responsibilities between host, client, shared components, and UI projects per service or module.

  • Server project: hosts the Blazor Web App, maps the root components, configures service defaults, static assets, antiforgery, HTTPS, MudBlazor services, and interactive rendering modes.
  • Client project: centralizes components that run on the WebAssembly side, routing, layout, menu structure, browser settings, localization resources, and integration with MudBlazor.
  • Shared WebApps project: centralizes common UI resources, notifications, dialogs, query string models, client results, culture/time zone services, and reusable components.
  • UI projects per service and module: when the solution has services and modules, Lino creates frontend projects inside the Web App structure to keep pages, menus, and resources isolated by context.

The generated host registers AddInteractiveServerComponents and AddInteractiveWebAssemblyComponents. The root components HeadOutlet and Routes use InteractiveAuto, allowing the application to start with server-side interactivity and migrate to WebAssembly when the client runtime is available.

Integration with backend APIs

When a Web App exists, Lino keeps the backend prepared for typed consumption by the frontend. Services and modules can receive Api.Contracts and Api.Client projects, exposing request/response contracts and typed clients for the generated endpoints.

  • The UI does not need to manually assemble raw HTTP calls for each feature.
  • Generated pages call typed API clients and work with request and response models created together with the backend.
  • Shared models such as ClientResult, QueryParams, and PagedQueryParams standardize result handling, filters, pagination, and visual feedback.
  • Integration through a typed HttpClient keeps the frontend aligned with the contracts, permissions, validation messages, and cultures used by the rest of the solution.

The practical flow becomes:

Blazor page -> typed API client -> Minimal API endpoint -> Command/Query -> database

This path preserves full-stack consistency: the domain model, use cases, endpoints, contracts, localized interface, navigation, and permissions follow the same project conventions.

Included UI resources

The generated Web App is already prepared for common concerns in real applications:

  • Routing through generated Routes.razor components.
  • Layout, navigation, and visual components based on MudBlazor.
  • Menu generation from the pages available in each service or module.
  • .resx files for localizing texts in the cultures configured in the project.
  • Support for authentication, token storage, protected routes, and permission services when the authentication feature is enabled.
  • Authorization-aware menus, showing entries only when the current user has the required permission.

Multiple frontends

A single backend can support more than one Web App. This is useful when different audiences need distinct navigation, security, experience, and deployment, while still sharing the same services, modules, use cases, and APIs.

  • Public site: pages accessible to customers, visitors, or users who are not authenticated yet.
  • Backoffice/Admin: internal management panel for operators, support, and administrative teams.
  • Partner portal: restricted access for partners, resellers, suppliers, or operational integrations.
  • Self-service area: experience focused on end customers, with its own menus and permissions.

Each frontend can evolve with its own layouts, resources, menus, and access rules, while the backend remains organized by services, modules, use cases, and contracts.

Generating web pages

After modeling the entity, defining the use cases, and generating commands, queries, endpoints, and persistence, the next step is usually to expose the feature in the interface. Lino automates this step with the page generator:

lino page new --service <ServiceName> --module <ModuleName> --entity <EntityName> --webapp <WebAppName>
lino page edit --service <ServiceName> --module <ModuleName> --entity <EntityName> --webapp <WebAppName>
lino page list --service <ServiceName> --module <ModuleName> --entity <EntityName> --webapp <WebAppName>

Options can also be provided interactively. The command asks for the service, module, entity, target Web App, page type, and properties that should appear in the grid. The most common type is CRUD; when the entity only supports queries, Lino can generate a DataGrid page.

Use page new to create the first version of the screen, page edit when the page needs to follow changes in fields, contracts, or behavior, and page list to understand what has already been generated for an entity.

Recommended flow

  1. Create or select the Web App with lino web-app new.
  2. Model the entity and generate the use cases, queries, commands, endpoints, API contracts, and persistence artifacts.
  3. Run lino page new and select the entity that will receive an interface.
  4. Choose the grid fields carefully. They become visible columns and help build query parameters, filters, and pagination.
  5. Run the solution through AppHost to validate UI, APIs, authentication, permissions, and database integration together.

Generated structure

For a CRUD page, Lino creates a complete Blazor page package instead of a single Razor file:

  • Registration or index page coordinating listing, creation, editing, details, and deletion.
  • Grid component based on MudBlazor, with server-side loading, pagination, filters, and mapping to query params.
  • Form component with a typed ViewModel and extensions to map create, update, and get-by-id requests.
  • Resources for titles, labels, buttons, validation messages, and localized interface texts.
  • Permission checks when authentication is enabled, including page access validation and conditional buttons.

Creating a CRUD page for the Vehicle entity, for example, in the Fleet service, Operations module, and Backoffice Web App, the structure follows this format:

src/WebApps/Backoffice/Services/Fleet/Operations/Pages/Vehicles/Registration/
β”œβ”€β”€ Vehicle.razor
β”œβ”€β”€ Vehicle.razor.cs
β”œβ”€β”€ Resources/
β”‚   β”œβ”€β”€ VehicleResources.resx
β”‚   └── VehicleResources.Designer.cs
└── Components/
    β”œβ”€β”€ Form/
    β”‚   β”œβ”€β”€ VehicleForm.razor
    β”‚   β”œβ”€β”€ VehicleForm.razor.cs
    β”‚   β”œβ”€β”€ VehicleFormExtensions.cs
    β”‚   β”œβ”€β”€ VehicleViewModel.cs
    β”‚   └── Resources/
    β”‚       β”œβ”€β”€ VehicleFormResources.resx
    β”‚       └── VehicleFormResources.Designer.cs
    └── Grid/
        β”œβ”€β”€ VehicleGrid.razor
        β”œβ”€β”€ VehicleGrid.razor.cs
        β”œβ”€β”€ VehicleGridExtensions.cs
        β”œβ”€β”€ VehiclePagedQueryParams.cs
        └── Resources/
            β”œβ”€β”€ VehicleGridResources.resx
            └── VehicleGridResources.Designer.cs

The idea preserves the previous structure used in examples such as Order: main page, code-behind, form components, grid components, extensions, and paged parameters. The current version adds resources and explicit integration with the organization by Web App, service, and module.

How the generated page works

The page uses the same contracts generated for the API. The grid turns filters and pagination into a listing request; the form turns the ViewModel into create or update requests; the typed API client sends the call to the corresponding endpoint. The endpoint executes the generated Command or Query and returns a typed response to the UI.

Frontend -> API -> Commands/Queries -> database

When authorization exists, the generated code also checks permissions such as listing, creation, editing, deletion, and lookup by identifier. This affects both page access and the visibility of actions for the user.

The result is a consistent full-stack flow: domain model, use case, endpoint, typed client, localized UI, navigation, and permissions follow the same project conventions. The generator reduces repetition, but the components remain editable for product, usability, and specific rule adjustments.

Multi-language interface

Web applications generated by Lino use .resx files for localized texts. This allows the same Razor structure to be kept while titles, labels, buttons, error messages, validation texts, and descriptions vary according to the active culture.

Localization should not be treated as a visual finishing step. In administrative pages and operational portals, inconsistent terminology creates doubt about the business rule. For this reason, resources are part of the UI architecture and should accompany pages, forms, grids, and shared components.

  • Keep keys stable, descriptive, and meaning-oriented, avoiding names tied to the temporary layout of the screen.
  • Avoid hard-coded text in pages and components; use resources for labels, messages, titles, breadcrumbs, menus, and confirmations.
  • Review technical terms to maintain consistency across documentation, CLI, API contracts, and interface.
  • Preserve the same set of keys across cultures to prevent unexpected fallback or partially translated screens.
  • Separate literal translation from product adaptation: when a term needs to be localized for the end user, keep the functional meaning of the action.

Generated pages usually have resources at the page, form, and grid levels. This separation allows each part to evolve without mixing listing, editing, validation, and navigation texts.

Responsive CRUD ready for use

The generated CRUD accelerates delivery because it creates the repetitive foundation of the screen: listing, form, API integration, resources, pagination, filters, actions, and code-behind. Even so, it should be reviewed as a product experience, not only as a technical scaffold.

A good CRUD screen must allow the user to find records, understand the current state, perform actions with confidence, and recover from failures. This applies both to internal backoffice screens and to portals aimed at customers or partners.

  • Choose listing columns that help decision-making; avoid grids with technical fields that mean nothing to the operator.
  • Configure filters and pagination according to the expected data volume and the user's main search criteria.
  • Review validations and error messages so they explain the problem in domain language, not only in implementation terms.
  • Ensure empty, loading, and failure states to avoid silent screens when there is no data, the API is slow, or an operation does not complete.
  • Confirm deletions and irreversible operations, making clear which record will be affected.
  • Show clear feedback after saving, editing, deleting, or failing, using localized notifications and messages.
  • Protect actions by permission on the backend and frontend; hiding buttons improves the experience, but does not replace real authorization.
  • Check responsiveness on smaller screens, especially action menus, filters, required fields, and grids with many columns.

The starting point generated by Lino should be treated as a coherent and integrated base. From there, the team adjusts visual density, field order, display rules, permissions, and texts to reflect the real product process.

An unhandled error has occurred. Reload πŸ—™