Result Page
FreeSuccess / error / warning / info / empty result pages with semantic SVG icons, a title, description and content/action projection slots.
</>sv-result resultstatusfeedback
import { SvResultComponent } from '@styloviz/overlays';Demo & usage
Basic usage
Usage example — open the gallery for the full interactive demo.
An interactive demo for this component lives in the gallery.
Open live demoMore examples
Error result
example.htmlAngular
3 lines
<sv-result status="error" title="Something went wrong" description="We couldn't load this page. Try again."> <sv-button label="Retry" (clicked)="reload()" /> </sv-result>
An error a support desk can act on
example.htmlAngular
23 lines
<!-- Bind the error and the page shows its correlation id with a copy button. "It said something went wrong" is an unanswerable ticket; the same sentence with a reference is one log query, and the id was already on the error - toSvDataError() reads it out of the body or the x-request-id header. focusHeading moves focus to the title on arrival: a routed error page otherwise leaves focus on whatever the user clicked, often an element that no longer exists. showDetails adds a collapsed Status/Code block - for an internal tool or a staging build. The error's "cause" is never rendered anywhere: it is a raw thrown value, and this is a screen people photograph. --> <sv-result status="error" title="Could not load the report" [description]="error()!.message" [error]="error()" [focusHeading]="true" [showDetails]="isInternalUser()" retryLabel="Try again" [retryAction]="reload" />
A retry that can fail again
example.htmlAngular
14 lines
<!-- A slotted button is right for "go home" — a link cannot fail. The retry is the one action that can, repeatedly, so it is first-class: [retryAction] makes the component own it, which means a second press is refused, the button shows a spinner, and a second failure is rendered instead of vanishing. Without the action, (retry) is emitted and the pending state is yours. --> <sv-result status="error" title="Could not load the report" retryLabel="Try again" [retryAction]="reloadReport" (retryFailed)="logFailure()"> <sv-button label="Back to dashboard" svResultActions /> </sv-result>
When to use
- Showing the outcome of a flow on its own screen: success, error, warning, info.
- Post-checkout, post-submit, 404/500 pages, access-denied screens.
- For an empty list within a page, use the Empty State instead.
Async states
- error
- retry
Inputs
| Name | Type | Default | Description |
|---|---|---|---|
| status | ResultStatus | 'info' | Semantic status that drives icon, colour palette and `role`. |
| title | string | '' | Primary headline. |
| description | string | '' | Supporting description. |
| icon | string | '' | Icon-library NAME overriding the status default. Was `iconPath`. |
| size | ResultSize | 'md' | Component size. |
| retryLabel | string | null | null | Label for a built-in retry button. `null` renders none. THE RETRY WAS ALWAYS SOMEONE ELSE'S BUTTON, WHICH IS WHY IT HAD NO STATE. Actions here are slotted content, so the retry on every error result in the kit was a host `<button>` with a `(click)` — and is the consequence: it can be pressed repeatedly, with no feedback, firing the failed request again and again. The page reports an outcome and then has nothing to say about the attempt to fix it. Slotted actions still work and are still right for "go home" or "contact support". This is for the one action whose whole point is that it might fail again. |
| retryAction | ResultRetryAction | null | null | The work the retry performs, run by the component. With it: repeated presses are refused (`createAsyncAction` is first-wins), the button shows a spinner while the work is in flight, and a rejection is rendered under it rather than disappearing. Without it, `(retry)` is emitted and the pending state is the host's problem. |
| retryPendingLabel | string | 'Retrying…' | Label while the retry is in flight. |
| retryPending | boolean | false | The host's own retry is in flight — for a host that listens to `(retry)` instead of passing `[retryAction]`. |
| error | SvDataError | null | null | The error being reported, for its diagnostics. AN ERROR PAGE WITH NO DIAGNOSTICS IS AN UNSUPPORTABLE TICKET. "It said something went wrong" is unanswerable: nobody can find the request. The status, the code and above all the server's correlation id are already on `SvDataError` — this input is what puts them on screen, where the user can read them out or copy them. THE `cause` IS NEVER RENDERED, and that is not an oversight. It is a raw thrown value — a stack trace, a response body, sometimes a token in a URL — and an error page is a screen users photograph and paste into chat. `message`, `status`, `code` and `reference` are the four fields that are safe to show and sufficient to find the request in a log. |
| copyLabel | string | null | 'Copy' | Label for the copy button beside the reference. `null` renders none. |
| copiedLabel | string | 'Copied' | Confirmation shown in place of `copyLabel` after a copy. |
| detailsLabel | string | 'Technical details' | Heading for the collapsed technical block. |
| showDetails | boolean | false | Show the status/code block. |
| focusHeading | boolean | false | Move focus to the heading when this page appears. |
Outputs
| Event | Payload type | Description |
|---|---|---|
| retry | void | The retry button was pressed. Always emitted, action or not. |
| retryFailed | void | The `[retryAction]` rejected. The message is rendered under the button. |
Customization & theming
- Pick a `status` (success, error, warning, info, empty) for the semantic icon and color.
- Diagnostics from the error itself: bind `[error]` and the server's correlation id renders with a copy button — "it said something went wrong" is an unanswerable ticket, and the same sentence with a reference is one log query. `[showDetails]` adds a collapsed status/code block for internal surfaces. `cause` is never rendered: it is a raw thrown value, and an error page is a screen people photograph.
- `[focusHeading]` moves focus to the title when the page appears, which is what a routed error page owes a keyboard or screen-reader user — the SPA swapped the view and left focus on an element that no longer exists. Opt-in, because `sv-result` is also used inline, where stealing focus interrupts something else.
- Override the glyph with `icon` (an icon-library name) and choose a `size`.
- Project body content and action buttons into the result slots.
- A first-class retry: `retryLabel` renders the button, `[retryAction]` hands the work to the component (first-wins, so repeated presses are refused; a spinner and `retryPendingLabel` while it runs; the rejection rendered under the button), and `(retry)` / `(retryFailed)` report what happened. Built on `createAsyncAction` in `@styloviz/core`.
Best practices
- Don't: Don't put raw diagnostics in `description` or the default slot — stack traces, SQL, internal hostnames, upstream status codes. Both are free-form and ungoverned, so nothing stops you; an error page is one of the most-screenshotted surfaces in any app.
- Do: Give the user one copyable reference — a correlation or request id — instead. It converts an unsupportable ticket into a log query, and it discloses nothing.
- Do: Map `status` to the real outcome so icon and color reinforce the message.
- Do: Give users a next step (primary action) rather than a dead end.
- Do: Use `retryLabel` with `[retryAction]` instead of a slotted retry button. A slotted button has no pending state, so it can be pressed repeatedly with no feedback — firing the request that just failed again and again.
- Tip: Keep the title outcome-focused and the description actionable.
Accessibility
- Status icons are decorative; the title carries the meaning as a heading.
- `[focusHeading]="true"` gives the title `tabindex="-1"` and focuses it once on arrival — `-1` so it does not become a permanent tab stop. Without it a routed error page leaves focus wherever the user clicked, often on an element that no longer exists, so the next Tab starts at the top of the page and a screen reader announces nothing.
- The copy confirmation is announced as well as shown: a sighted user sees the label change to "Copied", and an `aria-live="polite"` region says so for everyone else.
- Action buttons are labelled and keyboard reachable.
- Adequate contrast across statuses in light and dark.
- The retry button carries `aria-busy` and is disabled while its action runs, so a screen-reader user is not told to press a button that is already working.
Feedback on Result Page
Spotted a gap in this page, or something that didn't work as described? Comments are GitHub Discussions in the public styloviz/community repo, so posting needs a GitHub account.