AppFormSection
The form-page skeleton — titled sections, a responsive field grid, and an action bar.
<app-form-section> · <app-form-row> · <app-form-actions>Three small layout helpers so every settings/profile/billing form stops re-inventing its page structure with raw rows and columns: <app-form-section> groups related fields under a title and optional description, <app-form-row> lays fields out in a responsive grid, and <app-form-actions> holds the submit/cancel bar — optionally sticky at the viewport bottom.
They are structure only. Fields inside stay the ordinary <app-input> family; buttons stay <app-button>.
In a real screen
A company settings form as a product would build it: two sections, required fields, validation on Save and an action bar. Switch the section options above it, clear the company name or break the GSTIN, then press Save.
Playground
Every attribute across all three tag helpers, live. Each change re-renders the real tag helpers on the server, so the markup underneath is the exact call that produced the form above it.
AppFormSection — group related fields
Wraps a set of related fields with a section heading and optional description.
Contact Information
<app-form-section title="Contact Information">
<app-form-row>
<app-input label="Email" name="email" type="email" />
<app-input label="Phone" name="phone" />
</app-form-row>
</app-form-section>
Billing Address
Where you want invoices sent
<app-form-section title="Billing Address" description="Where you want invoices sent">
<app-form-row>
<app-input label="Street" name="street" />
<app-input label="City" name="city" />
</app-form-row>
</app-form-section>
Layout, labels and density
Three section options for longer forms. Each folds back to the stacked, labels-on-top look on smaller screens, so a form never squeezes on a phone.
Notifications
Where we send alerts.
<app-form-section title="Notifications" description="Where we send alerts." layout="side">
<app-form-row>
<app-input label="Email" name="email" type="email" />
<app-input label="Phone" name="phone" />
</app-form-row>
</app-form-section>
Account
<app-form-section title="Account" labels="start">
<app-form-row>
<app-input label="Display name" name="displayName" />
<app-input label="Email" name="email" type="email" hint="We never share it." />
</app-form-row>
</app-form-section>
Filters
<app-form-section title="Filters" density="compact">
<app-form-row columns="3">
<app-input label="Customer" name="customer" />
<app-input label="Invoice" name="invoice" />
<app-input label="Amount" name="amount" />
</app-form-row>
</app-form-section>
AppFormRow — responsive field grid
Arranges fields in a grid with responsive column collapse.
<app-form-row columns="2">
<app-input label="First name" name="firstName" />
<app-input label="Last name" name="lastName" />
</app-form-row>
<app-form-row columns="1">
<app-input label="Full width field" name="fullName" />
</app-form-row>
<app-form-row columns="3">
<app-input label="Day" name="day" />
<app-input label="Month" name="month" />
<app-input label="Year" name="year" />
</app-form-row>
AppFormActions — button footer
Holds action buttons at a form's end — typically a submit/cancel pair.
<app-form-actions>
<app-button variant="primary" type="submit">Save</app-button>
</app-form-actions>
<app-form-actions align="start">
<app-button variant="outline" type="button">Cancel</app-button>
</app-form-actions>
<app-form-actions align="between">
<app-button variant="outline" type="button">Cancel</app-button>
<app-button variant="primary" type="submit">Submit</app-button>
</app-form-actions>
<app-form-actions sticky="true">
<app-button variant="primary" type="submit">Confirm</app-button>
</app-form-actions>
Composing a complete form
Combine the three helpers to build a standard form layout — sections, rows within each, actions at the end.
<app-form-section title="Profile" description="How you appear to your team.">
<app-form-row columns="2">
<app-input label="First name" name="first" />
<app-input label="Last name" name="last" />
</app-form-row>
</app-form-section>
<app-form-actions align="end">
<app-button variant="secondary" type="button">Cancel</app-button>
<app-button variant="primary" type="submit">Save changes</app-button>
</app-form-actions>
On a long form, sticky="true" pins the action bar to the viewport bottom with a top border and surface background, so Save is always reachable.
Responsive & accessible
All three helpers are fully responsive and accessible — grids collapse to 1 column on mobile, buttons stack when needed, sticky footers keep actions visible.
- app-form-row collapses below the
mdbreakpoint, letting each field take full width on phones. - app-form-actions uses flexbox to align and wrap buttons naturally; sticky mode adds a top border so users know what's pinned.
- app-form-section stacks vertically and leaves room for tall content, maintaining a readable column width on all screens.
When to use
Use it when
- You're building a form page — use all three helpers together for a consistent layout.
- You need to group logically related fields — AppFormSection wraps them with a title.
- You have multiple fields in a row — AppFormRow arranges them responsively.
- You're adding action buttons (submit, cancel, etc.) — AppFormActions keeps them aligned.
Reach for something else when
- You're placing a single isolated field. → just use AppInput directly
- Your form is a dialog/modal. → AppModal / AppConfirmDialog owns the footer logic
- You need a custom grid layout. → use Bootstrap Row/Col for full control
Every option
The whole surface of all three helpers, one cell per value.
app-form-section — title / description combinations
(no title, no description)title onlyPreferences
description onlyA description renders on its own too
title + descriptionPreferences
Control how you are notified
app-form-section — layout, labels and density, every value
layout="stacked"Profile
Your public details.
layout="side"Profile
Your public details.
labels="top"Profile
Your public details.
labels="start"Profile
Your public details.
density="comfortable"Profile
Your public details.
density="compact"Profile
Your public details.
app-form-row — columns, every value
columns="1"columns="2"columns="3"app-form-actions — align, every value
align="end"align="start"align="between"app-form-actions — sticky
(default) — flows with the pagesticky="true" — pinned to the bottom of the scroll container with a top bordersticky="true" align="between"Attributes
app-form-section
| Attribute | Type | Default | Description |
|---|---|---|---|
| title | string | Section heading. | |
| description | string | Intro text under the title. Renders on its own when there is no title. | |
| layout | stacked | side | stacked | side puts the title and description in a column beside the fields from lg up; below that the section stacks. |
| labels | top | start | top | start puts each label beside its control, one field per row, from md up; below that labels sit on top. |
| density | comfortable | compact | comfortable | compact uses 36px controls and tighter gaps, for admin screens and dialogs. |
app-form-row
| Attribute | Type | Default | Description |
|---|---|---|---|
| columns | 1 | 2 | 3 | 2 | Field columns on desktop; collapses to one under the md breakpoint. |
app-form-actions
| Attribute | Type | Default | Description |
|---|---|---|---|
| sticky | boolean | false | Pin the bar to the viewport bottom with a top border and surface background. |
| align | end | start | between | end | Horizontal placement of the buttons. |