Result Page

Free

Success / 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 demo

More examples

Error result

example.htmlAngular
<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
<!-- 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
<!-- 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

NameTypeDefaultDescription
statusResultStatus'info'Semantic status that drives icon, colour palette and `role`.
titlestring''Primary headline.
descriptionstring''Supporting description.
iconstring''Icon-library NAME overriding the status default. Was `iconPath`.
sizeResultSize'md'Component size.
retryLabelstring | nullnullLabel 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.
retryActionResultRetryAction | nullnullThe 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.
retryPendingLabelstring'Retrying…'Label while the retry is in flight.
retryPendingbooleanfalseThe host's own retry is in flight — for a host that listens to `(retry)` instead of passing `[retryAction]`.
errorSvDataError | nullnullThe 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.
copyLabelstring | null'Copy'Label for the copy button beside the reference. `null` renders none.
copiedLabelstring'Copied'Confirmation shown in place of `copyLabel` after a copy.
detailsLabelstring'Technical details'Heading for the collapsed technical block.
showDetailsbooleanfalseShow the status/code block.
focusHeadingbooleanfalseMove focus to the heading when this page appears.

Outputs

EventPayload typeDescription
retryvoidThe retry button was pressed. Always emitted, action or not.
retryFailedvoidThe `[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.

Loads giscus.app and GitHub — nothing is fetched until you click.