display
Alert
An inline message with a tone, a title, a description and optional actions.
Examples
Tones
Each tone brings its own icon. Pass `icon={null}` to drop it.
Scheduled maintenance
The API will be read-only on Sunday from 02:00 UTC.
Payment received
Your invoice for March has been settled.
Approaching your quota
You have used 92% of this month’s builds.
Deploy failed
Two of six health checks did not pass.
With actions
Your trial ends in 3 days
Add a payment method to keep your projects running after Friday.
Own the source
Noksha ships as a package and as copy-paste source. Take the files and they are yours to change. These are read from the generated registry, so they are exactly what the library ships — never a paraphrase of it.
Take these files, and internal helpers along with them — the imports below point at them. Or let the CLI do it: npx @noksha-ui/cli add alert writes them, follows the same dependency graph, and fixes up the imports.
import { Slot } from '@noksha-ui/core';
import * as React from 'react';
import type { Tone } from '../../internal/tone.js';
import type { AlertPartProps, AlertProps } from './alert.types.js';
import {
alertActionsVariants,
alertDescriptionVariants,
alertTitleVariants,
alertVariants,
} from './alert.variants.js';
const iconProps = {
viewBox: '0 0 24 24',
fill: 'none',
stroke: 'currentColor',
strokeWidth: 2,
strokeLinecap: 'round',
strokeLinejoin: 'round',
'aria-hidden': true,
} as const;
/**
* Shape carries the meaning as well as colour does — a triangle reads as a
* warning to someone who cannot tell the amber from the red.
*/
const TONE_ICONS: Record<Tone, React.ReactNode> = {
info: (
<svg {...iconProps} aria-hidden="true">
<circle cx="12" cy="12" r="9" />
<path d="M12 11v5M12 7.75v.5" />
</svg>
),
success: (
<svg {...iconProps} aria-hidden="true">
<circle cx="12" cy="12" r="9" />
<path d="M8.5 12.5l2.5 2.5 4.5-5" />
</svg>
),
warning: (
<svg {...iconProps} aria-hidden="true">
<path d="M10.3 4.2 2.8 17.1A2 2 0 0 0 4.5 20h15a2 2 0 0 0 1.7-2.9L13.7 4.2a2 2 0 0 0-3.4 0Z" />
<path d="M12 9.5v4M12 16.75v.5" />
</svg>
),
danger: (
<svg {...iconProps} aria-hidden="true">
<circle cx="12" cy="12" r="9" />
<path d="M12 7.5v5M12 15.75v.5" />
</svg>
),
accent: (
<svg {...iconProps} aria-hidden="true">
<circle cx="12" cy="12" r="9" />
<path d="M12 11v5M12 7.75v.5" />
</svg>
),
neutral: (
<svg {...iconProps} aria-hidden="true">
<circle cx="12" cy="12" r="9" />
<path d="M12 11v5M12 7.75v.5" />
</svg>
),
};
/**
* A message about the state of the page or of an action.
*
* ```tsx
* <Alert.Root tone="danger" live>
* <Alert.Title>Deployment failed</Alert.Title>
* <Alert.Description>Build step 3 exited with code 1.</Alert.Description>
* <Alert.Actions><Button size="sm">Retry</Button></Alert.Actions>
* </Alert.Root>
* ```
*/
export const AlertRoot = React.forwardRef<HTMLDivElement, AlertProps>(function AlertRoot(
{
variant = 'soft',
tone = 'info',
live = false,
icon,
asChild = false,
className,
children,
...rest
},
ref,
) {
const Comp = asChild ? Slot : 'div';
const resolvedIcon = icon === undefined ? TONE_ICONS[tone] : icon;
return (
<Comp
ref={ref}
// `role="alert"` is an assertive live region. Reserving it for `live`
// keeps a static page banner from interrupting the screen reader with
// something that was already there before the user arrived.
role={live ? 'alert' : undefined}
data-tone={tone}
className={alertVariants({ variant, tone, withIcon: resolvedIcon != null, className })}
{...rest}
>
{resolvedIcon}
{children}
</Comp>
);
});
AlertRoot.displayName = 'Alert.Root';
export const AlertTitle = React.forwardRef<HTMLDivElement, AlertPartProps>(function AlertTitle(
{ asChild = false, className, ...rest },
ref,
) {
const Comp = asChild ? Slot : 'div';
return <Comp ref={ref} className={alertTitleVariants({ className })} {...rest} />;
});
AlertTitle.displayName = 'Alert.Title';
export const AlertDescription = React.forwardRef<HTMLDivElement, AlertPartProps>(
function AlertDescription({ asChild = false, className, ...rest }, ref) {
const Comp = asChild ? Slot : 'div';
return <Comp ref={ref} className={alertDescriptionVariants({ className })} {...rest} />;
},
);
AlertDescription.displayName = 'Alert.Description';
export const AlertActions = React.forwardRef<HTMLDivElement, AlertPartProps>(function AlertActions(
{ asChild = false, className, ...rest },
ref,
) {
const Comp = asChild ? Slot : 'div';
return <Comp ref={ref} className={alertActionsVariants({ className })} {...rest} />;
});
AlertActions.displayName = 'Alert.Actions';
export const Alert = {
Root: AlertRoot,
Title: AlertTitle,
Description: AlertDescription,
Actions: AlertActions,
};
export { alertVariants };API reference
Accepted values
- AlertVariant
softoutlinesolid
AlertProps
Also accepts everything from React.HTMLAttributes<HTMLDivElement>.
| Prop | Type | Description |
|---|---|---|
| variant | AlertVariant | — |
| tone | AlertTone | — |
| live | boolean | Announce the alert the moment it appears, interrupting the screen reader. Off by default. `role="alert"` is an assertive live region, and using it for a banner that was already on the page when it loaded — a plan notice, a deprecation warning — interrupts the user for something that is not news. Turn it on for messages that appear in response to an action. |
| icon | React.ReactNode | null | Replaces the tone's default icon. Pass `null` for no icon at all. |
| asChild | boolean | — |
AlertPartProps
Also accepts everything from React.HTMLAttributes<HTMLDivElement>.
| Prop | Type | Description |
|---|---|---|
| asChild | boolean | — |