500 Server Error
blocklayoutFull-page 500 with an isometric crashed-server illustration, HTTP code, headline, and Try again + Back to home + Contact support. Prop-driven: pass title, description, image, action labels, hrefs, and layout (`page` | `contained`). Pass `image=""` to hide the art.
Installation
$pnpm dlx shadcn@latest add https://uipkge.dev/r/lit/error-500.json$npx shadcn@latest add https://uipkge.dev/r/lit/error-500.json$yarn dlx shadcn@latest add https://uipkge.dev/r/lit/error-500.json$bunx shadcn@latest add https://uipkge.dev/r/lit/error-500.jsonManifest:
https://uipkge.dev/r/lit/error-500.jsonInstalls to:src/uip/blocks/error-500/Variants
Loading interactive previews…
Loading interactive previews…
Props
| Name | Type / Values | Default | Required |
|---|---|---|---|
code | string | — | optional |
heading | string | — | optional |
description | string | — | optional |
image | string | /illustrations/error-500.jpg | optional |
image-alt | string | — | optional |
primary-label | string | — | optional |
secondary-label | string | — | optional |
tertiary-label | string | — | optional |
primary-href | string | — | optional |
secondary-href | string | — | optional |
tertiary-href | string | — | optional |
show-primary | boolean | true | optional |
show-secondary | boolean | true | optional |
show-tertiary | boolean | true | optional |
primary-disabled | boolean | false | optional |
secondary-disabled | boolean | false | optional |
tertiary-disabled | boolean | false | optional |
layout | 'page' | 'contained' | page | optional |
Schema
Type aliases exported from this item's source. Use these to shape the data you pass in.
HTMLElementTagNameMapinterface HTMLElementTagNameMap {
'uip-error-500': UipError500
}
}Files installed (1)
src/uip/blocks/error-500/error-500.ts6.6 kB
import { LitElement, css, html, nothing } from 'lit' import { LifeBuoy, RefreshCcw } from 'lucide' import { cn } from '../../lib/utils' import { tailwind } from '../../lib/styles' import { ThemeController } from '../../lib/theme' import { icon } from '../../lib/icon' // Side-effect import: registers <uip-button> for standalone use (consumers // who install only this block still get a working button). import '../../components/button/button' const falseConverter = { fromAttribute: (v: string | null) => v !== null && v !== 'false' } /** * <uip-error-500> — the registry Error500 block as a web component. * * Class strings are React's Error500 verbatim. `heading` is React's `title` * (`title` is taken by HTMLElement). `onPrimary` / `onSecondary` / * `onTertiary` become `primary` / `secondary` / `tertiary` CustomEvents * (bubbles, composed). * * Deviation: React's `image === undefined` HTTP-panel branch is unreachable * (the default param fills `image` first), so it is not ported: omit `image` * for the default art, pass `image=""` to hide it. */ export class UipError500 extends LitElement { static shadowRootOptions = { ...LitElement.shadowRootOptions, delegatesFocus: true } // :host display can't be a utility class (the host has no template of its own). static styles = [tailwind, css`:host { display: block; }`] static properties = { code: {}, heading: {}, description: {}, image: {}, imageAlt: { attribute: 'image-alt' }, primaryLabel: { attribute: 'primary-label' }, secondaryLabel: { attribute: 'secondary-label' }, tertiaryLabel: { attribute: 'tertiary-label' }, primaryHref: { attribute: 'primary-href' }, secondaryHref: { attribute: 'secondary-href' }, tertiaryHref: { attribute: 'tertiary-href' }, showPrimary: { type: Boolean, attribute: 'show-primary', converter: falseConverter }, showSecondary: { type: Boolean, attribute: 'show-secondary', converter: falseConverter }, showTertiary: { type: Boolean, attribute: 'show-tertiary', converter: falseConverter }, primaryDisabled: { type: Boolean, attribute: 'primary-disabled' }, secondaryDisabled: { type: Boolean, attribute: 'secondary-disabled' }, tertiaryDisabled: { type: Boolean, attribute: 'tertiary-disabled' }, layout: { reflect: true }, } code?: string heading?: string description?: string image = '/illustrations/error-500.jpg' imageAlt = '' primaryLabel?: string secondaryLabel?: string tertiaryLabel?: string primaryHref?: string secondaryHref?: string tertiaryHref?: string showPrimary = true showSecondary = true showTertiary = true primaryDisabled = false secondaryDisabled = false tertiaryDisabled = false layout: 'page' | 'contained' = 'page' constructor() { super() new ThemeController(this) } connectedCallback() { super.connectedCallback() this.setAttribute('data-uipkge', '') this.setAttribute('data-slot', 'error-500') this.setAttribute('data-layout', this.layout) } willUpdate() { this.setAttribute('data-layout', this.layout) } private emit(name: 'primary' | 'secondary' | 'tertiary') { this.dispatchEvent(new CustomEvent(name, { bubbles: true, composed: true })) } render() { const codeText = this.code ?? '500' const titleText = this.heading ?? 'Something went wrong' const descriptionText = this.description ?? 'We hit an unexpected error on our side. Try again in a moment — if it keeps happening, our team is on it.' const primaryText = this.primaryLabel ?? 'Try again' const secondaryText = this.secondaryLabel ?? 'Back to home' const tertiaryText = this.tertiaryLabel ?? 'Contact support' return html` <section data-slot="error-500" data-layout=${this.layout} class=${cn( 'bg-background relative flex items-center overflow-hidden px-6 py-16', this.layout === 'page' ? 'min-h-svh' : 'min-h-[28rem]', )} > <div class="mx-auto grid w-full max-w-5xl items-center gap-10 lg:grid-cols-2 lg:gap-16"> <div> ${codeText ? html`<p class="text-muted-foreground text-sm font-medium">HTTP ${codeText}</p> <p class="mt-3 text-6xl font-semibold tracking-tighter sm:text-7xl">${codeText}</p>` : nothing} <h1 class="mt-3 text-2xl font-semibold tracking-tight sm:text-3xl">${titleText}</h1> ${descriptionText ? html`<p class="text-muted-foreground mt-4 max-w-md text-base">${descriptionText}</p>` : nothing} ${this.showPrimary || this.showSecondary || this.showTertiary ? html`<div class="mt-8 flex flex-wrap items-center gap-3"> ${this.showPrimary ? html`<uip-button size="lg" ?disabled=${this.primaryDisabled} href=${this.primaryHref ?? nothing} @click=${() => this.emit('primary')} > ${icon(RefreshCcw, 'refresh-ccw')} ${primaryText} </uip-button>` : nothing} ${this.showSecondary ? html`<uip-button size="lg" variant="outline" ?disabled=${this.secondaryDisabled} href=${this.secondaryHref ?? nothing} @click=${() => this.emit('secondary')} > ${secondaryText} </uip-button>` : nothing} ${this.showTertiary ? html`<uip-button size="lg" variant="ghost" ?disabled=${this.tertiaryDisabled} href=${this.tertiaryHref ?? nothing} @click=${() => this.emit('tertiary')} > ${tertiaryText} ${icon(LifeBuoy, 'life-buoy')} </uip-button>` : nothing} </div>` : nothing} </div> ${this.image ? html`<div class="border-border bg-muted overflow-hidden rounded-xl border"> <img src=${this.image} alt=${this.imageAlt} class="aspect-[4/3] w-full object-cover" /> </div>` : nothing} </div> </section> ` } } customElements.get('uip-error-500') || customElements.define('uip-error-500', UipError500) declare global { interface HTMLElementTagNameMap { 'uip-error-500': UipError500 } }
Raw manifest:https://uipkge.dev/r/lit/error-500.json