On this page
Notification
Notifications notify the user with the status of an action.
- Overview
- Rules and guidance
- Properties
- Styles
- Accessibility
When to use
- To inform the user about the result of an action they have taken
- To inform the user about a system change
- To draw the user's attention to something important, timely and relevant to their current task
When not to use
- For static contextual information not tied to an action or system event - use plain text instead
- For an absence of data - use Empty State instead
- While data is loading - use Skeleton Loading instead
- For short statuses attached to objects - use Badge instead
- More than once per page - Notifications are disruptive. If overused, users will stop noticing them. If multiple are unavoidable, see Grouping below.
Anatomy

- Icon
- Title
- Description
- Actions
- Close button
- Error list - only present in ErrorSummaryNotification
Basic usage
Edit live code
<Notification id="basic-example" title="Meter read being processed"> <P>It may take up to 60 seconds to appear in the reads table once submitted</P> </Notification>
Variants and features
Standard variants
Notifications use colour and icons to convey the tone and priority of the information they contain. There are four variants:
- Info - provide additional information relevant to the current context
- Success - confirm an action was completed as expected
- Warning - let the user know they are taking an action that is undesirable, or which might have unexpected results
- Error - explain something has gone wrong, and what, if anything, the user can do to correct the problem
The colours and icons are tied to each variant and should not be changed. Do not override the icon or create custom colour combinations - the icon ensures meaning is not communicated by colour alone.
Edit live code
<> <Notification id="example-info" variant="info"> <P>Meter reads take up to 60 seconds to appear in the reads table once submitted</P> </Notification> <Notification id="example-success" variant="success"> <P>Meter read submitted</P> </Notification> <Notification id="example-warning" variant="warning"> <P>Submitting this meter read will cause this customer to be re-billed</P> </Notification> <Notification id="example-error" variant="error"> <P>Meter read not submitted</P> </Notification> </>
Error summary
ErrorSummaryNotification is a separate component used to summarise all validation errors on a form. Always render it on the page and pass errors to it when the form is submitted - it remains hidden until errors are received.
With correct use, the following happens automatically:
- Keyboard focus moves to the error summary when it appears
- Each listed error links to its respective field
Edit live code
<Inert /* Ignore - prevent auto-focus for this example. */> <ErrorSummaryNotification id="error-summary" errors={[ { id: 'error-name', message: 'An error message goes here' } ]} /> </Inert>
In a real form, pass the errors when the form is submitted. The summary stays hidden until it receives them, then takes focus and links to the field:
Edit live code
() => { // ignore - for live code example only. const Example = () => { const [errors, setErrors] = useState([]); const onSubmit = (event) => { event.preventDefault(); setErrors([{ id: 'example', message: 'Enter a name' }]); } return ( <> <Heading2>Example form</Heading2> <ErrorSummaryNotification id="error-summary-dynamic" errors={errors} /> <form noValidate onSubmit={onSubmit}> <Stack spaceBetween={4}> <TextField id="example" label="Name" error={getErrorMessage('example', errors)} /> <PrimaryCTAButton type="submit">Show errors</PrimaryCTAButton> </Stack> </form> </> ); } return <Example /> }
Dismissal
Notifications are optionally dismissible via the dismissable prop. Do not make a notification dismissible if it is critical for the user to read or interact with, or if they may need to refer back to it during their task.
Use the onDismiss prop to perform an action when the notification is dismissed - for example, updating local storage. Consider whether the notification should be dismissed forever on this device, until the next session, or until the next page load.
Edit live code
<Notification id="dismissible-example" title="We are processing the meter read" dismissable onDismiss={() => console.log('Notification dismissed')} > <P>It can take 30 seconds or more to update the account</P> </Notification>
Single line
For short notifications, title and description can be combined on a single line to save space. Omit the title prop and make the opening words of the description bold, separated from the remaining text by a non-bold hyphen.
Edit live code
<Notification id="single-line-example" variant="success"> <P><Strong>Meter read submitted</Strong> - it may take 60 seconds to appear in the reads table</P> </Notification>
Actions
Notifications can include up to two actions. Actions are buttons and should not be used for navigation - use a TextLink in the description instead. Do not use an action for dismissal; use the dismissable prop instead.
Edit live code
<Notification id="actions-example" title="Meter read being processed" actions={[ { label: 'Refresh reads table', id: 'refetch', onClick: () => alert('Refetch action clicked'), }, ]}> <P>It may take up to 60 seconds to appear in the reads table once submitted</P> </Notification>
Grouping
Use no more than one Notification per page where possible. On pages with multiple independent panels, limit to one per panel.
If multiple notifications in the same place are unavoidable, stack them vertically in order of importance. Never place notifications side by side.
usage
- Notification will use the variant's predetermined colour tokens. There is no prop to change themCopy rule ID for notification.will-variant-colours
- Notification will render the icon tied to the current variant. The icon cannot be removed or overriddenCopy rule ID for notification.will-variant-icon
- Notification must not be used to represent an absence of dataUse EmptyState instead. An absence of data is not a status event. Using a notification here misrepresents what has happened.Copy rule ID for notification.no-empty-state
- Notification must not be used while data is loadingUse Skeleton Loading instead. A notification during loading implies a status change has occurred, which is misleading.Copy rule ID for notification.no-loading-state
placement
- If there are validation errors on form submissionErrorSummaryNotification must be placed at the top of the formCopy rule ID for notification.error-summary-placement
- Notification should be placed close to the content it relates toNotifications that are physically distant from the content they describe are easy to miss and create a confusing relationship between the message and the thing it refers to.Copy rule ID for notification.place-near-content
- If used for site-wide information such as upcoming maintenanceNotification should be placed at the very top of the page, above all headingsSite-wide messages affect the entire page and must be seen before the user engages with any content.Copy rule ID for notification.site-wide-placement
- If conveying information the user needs to know before they start a formNotification should be placed at the top of the form before any fieldsUsers should have context before they begin entering data, not discover it partway through. Use sparingly. Consider whether a notification is necessary at all.Copy rule ID for notification.form-pre-task-placement
- If showing a conditional warning based on data the user has enteredNotification may be placed directly above the submit buttonAlways consider whether the form design can be changed to avoid this scenario.Copy rule ID for notification.form-conditional-warning-placement
content
- Notification error message must tell the user what has gone wrong and, where possible, how to fix itAn error message that describes the problem without offering a path forward leaves the user stuck.Copy rule ID for notification.error-message.explain-and-resolve
- ErrorSummaryNotification error message must use the same wording as the inline field error for the same fieldInconsistent wording between the summary and the field error creates confusion about whether they refer to the same problem.Exception: the inline error contains a link, in which case omit the link from the summary versionCopy rule ID for error-summary-notification.error-message.match-inline-wording
- Notification title must not end with a full stop or exclamation markCopy rule ID for notification.title.no-terminal-punctuation
- If it is a single sentenceNotification description must not end with a full stopCopy rule ID for notification.description.no-terminal-full-stop
- Notification error message must not use vague or ambiguous language such as 'an error occurred' or 'invalid' without explanationUsers need to understand what went wrong and what to do next. Vague messages leave them unable to act.Copy rule ID for notification.error-message.no-vague-language
- Notification error message must not use whimsical language such as 'oops'Trivialises an error and undermines trust.Copy rule ID for notification.error-message.no-whimsy
- Notification error message must not sound accusatory or make the user feel at faultCopy rule ID for notification.error-message.not-accusatory
- Notification title should be no more than 5 wordsTitles must be scannable. Users in task-focused contexts will not read long headings.Copy rule ID for notification.title.max-five-words
- Notification title should be a statement, not a questionQuestions create ambiguity about what has happened. Statements are clearer and more direct.Copy rule ID for notification.title.statement-not-question
- If the notification confirms a completed actionNotification title should use past tense to indicate completed actionsPast tense ('submitted', 'sent') clearly signals completion. Present tense is ambiguous about whether the action is still in progress.Copy rule ID for notification.title.past-tense-for-completed-actions
- ErrorSummaryNotification title should remain as 'There is a problem'Consistent error summary titles help users recognise the pattern across the platform.Exception: there is a strong reason to change itCopy rule ID for error-summary-notification.title.default-wording
- Notification description should not contain bold textBold text in the description competes with the title and disrupts the visual hierarchy. The component already sets the hierarchy. Title renders bold, description renders plain.Copy rule ID for notification.description.no-bold
- Notification error message should not use pleasantries such as 'please' or 'sorry'Adds length without adding meaning, and Nebula ships into client-branded apps whose voice may differ. See the content writing guide for Kaluza's default position.Copy rule ID for notification.error-message.no-pleasantries
- Notification title should not use possessive adjectives such as 'your' or subjects such as 'the system' when context is clearThese add length without adding meaning.Copy rule ID for notification.title.no-possessive-or-subject
- Notification title should not use unnecessary qualifiers such as 'successfully' when the positive outcome is already impliedBrevity aids scannability. If 'sent' already implies success, 'successfully sent' is redundant.Copy rule ID for notification.title.no-unnecessary-qualifiers
- Notification description should not repeat or paraphrase the titleThe description should add information, not restate it. Repetition wastes the user's time and dilutes both messages.Copy rule ID for notification.description.no-title-repetition
- Notification description should not contain questionsQuestions in notifications are confusing. The user does not know if they are expected to answer, or if it is rhetorical.Copy rule ID for notification.description.no-questions
interaction
- Notification will apply role=alert for the error variant and role=status for all other variants. This can be overridden with the role prop if there is a clear accessibility justificationCopy rule ID for notification.will-role-by-variant
- ErrorSummaryNotification will move keyboard focus to itself when the errors array becomes non-empty. The consumer does not need to call focus manuallyCopy rule ID for notification.will-error-summary-autofocus
- Notification will be removed from the DOM when dismissed. The parent must remount it to make it reappearCopy rule ID for notification.will-unmount-on-dismiss
- Notification must always be rendered in the DOM even when it has no contentScreen readers need to anticipate where notifications will appear. The component renders nothing visually when empty, but its presence in the DOM means announcements are heard in the right place.Copy rule ID for notification.always-in-dom
- Notification must not be made dismissible if the user needs to read or interact with it to complete their task, or may need to refer back to itOnce dismissed the notification is removed from the DOM. If the user needs it again, they have no way to retrieve it.Copy rule ID for notification.no-dismissal-if-critical
- Notification actions must not be used for dismissalUse the dismissable prop instead. Overloading an action button with dismissal creates an inconsistent pattern.Copy rule ID for notification.actions.no-dismissal
- Notification actions must not be used for navigation to another pageActions are buttons. Use a TextLink inside the notification description instead.Copy rule ID for notification.actions.no-navigation
- Notification should not have more than two actionsMore than two actions crowds the notification and obscures the primary action. The component does not enforce this limit in code.Copy rule ID for notification.max-two-actions
- Notification should not receive programmatic focus when dynamically revealedThe live region (role=status or role=alert) announces the message to screen readers without focus moving. Moving focus interrupts the user's current context. Cases where focus movement is needed. Such as an error summary after form submission. Are handled automatically by ErrorSummaryNotification and Form, not by the base Notification.Exception: the notification requires the user to read or act before they can continueCopy rule ID for notification.focus-on-reveal
grouping
- Notification must not be placed side-by-side with another NotificationSide-by-side notifications compete visually and make it harder for users. Particularly screen reader users. To understand the relationship and priority of each message.Exception: used in a do and don't listCopy rule ID for notification.no-side-by-side
- Notification should not appear more than once on a pageMultiple notifications compete for attention and reduce the impact of each. Overuse leads to alert fatigue.Exception: the page contains multiple independent panels, in which case no more than one Notification per panelCopy rule ID for notification.max-one-per-page
Notification
Also accepts all props from HTMLAttributes.
| Name | Type | Default | Required | Description |
|---|---|---|---|---|
id | string | — | Yes | Set as the `id` of the notification's wrapper element. |
title | ReactNode | — | No | Short bold text shown above the content. The notification renders nothing if it has neither a `title` nor children. |
noMargin | boolean | — | No | Removes the default margin below the notification. |
variant | error success info warning | 'info' | No | The kind of message. Sets the colour and icon, and the default ARIA role (`alert` for `error`, `status` for the rest). |
dismissable | boolean | — | No | Shows a dismiss button. Clicking it removes the notification from the page without any state needed from the parent. |
onDismiss | () => void | — | No | Called when the user dismisses the notification. |
actions | Action[] | — | No | Follow-up actions shown as buttons below the content. Only shown when the notification also has a `title` or children. |
ErrorSummaryNotification
Also accepts all props from HTMLAttributes.
| Name | Type | Default | Required | Description |
|---|---|---|---|---|
id | string | — | Yes | Set as the `id` of the notification's wrapper element. |
title | ReactNode | <>There is a problem</> | No | Short bold text shown above the content. The notification renders nothing if it has neither a `title` nor children. |
noMargin | boolean | — | No | Removes the default margin below the notification. |
dismissable | boolean | — | No | Shows a dismiss button. Clicking it removes the notification from the page without any state needed from the parent. |
onDismiss | () => void | — | No | Called when the user dismisses the notification. |
actions | Action[] | — | No | Follow-up actions shown as buttons below the content. Only shown when the notification also has a `title` or children. |
errors | ErrorSummaryList | — | Yes | The errors to list. Each `message` is shown as a link to the element whose `id` matches the error's `id` (usually the invalid field). While the list is empty the notification renders nothing; when errors arrive it appears with the `title` (`There is a problem` unless set) and takes focus. Keep the same array between renders unless the errors change - a new array moves focus to the notification again. |
Styles
margin-bottom
Defined in Notification/Notification.tsx
@when !noMargin {
margin-bottom: ${space[6]};
}
focus-wrapper
Defined in Notification/Notification.tsx
&:focus-visible {
${focusRing}
}
border-radius: ${radii.standard};
notification-content
Defined in Notification/Notification.tsx
display: flex;
flex-direction: column;
gap: ${space[1]};
font-weight: ${fontWeights.normal};
font-family: ${fonts.body};
font-size: ${responsiveFontSizes.body[0]};
color: ${colors.body};
${mediaQueries.smallAndUp} {
font-size: ${responsiveFontSizes.body[1]};
}
icon
Defined in Notification/Notification.tsx
flex-shrink: 0;
font-size: ${space[5]};
color: ${variants[variant].foreground};
line-indent
Defined in Notification/Notification.tsx
position: absolute;
top: -${borderWidths.standard};
bottom: -${borderWidths.standard};
left: -${borderWidths.standard};
width: ${borderWidths.heavier};
border-radius: ${radii.standard} 0 0 ${radii.standard};
background-color: ${variants[variant].foreground};
notification-box
Defined in Notification/Notification.tsx
display: flex;
gap: ${space[2]};
border: ${borderWidths.standard} solid ${currentVariant.foreground};
background-color: ${currentVariant.background};
border-radius: ${radii.standard};
position: relative;
padding: ${space[2]};
padding-left: ${space[2] + borderWidths.heavier};
padding-right: ${dismissable ? numberToRem(remToNumber(space[2]) + remToNumber(space[10])) : space[2]};
dismiss-button
Defined in Notification/Notification.tsx
background: none;
border: none;
padding: 0;
width: ${space[10]};
height: ${space[10]};
position: absolute;
color: ${colors.heading};
top: 0;
right: 0;
&:focus-visible {
${focusRing}
}
error-list
Defined in ErrorSummaryNotification/ErrorSummaryNotification.tsx
&:first-child {
margin-top: ${space[1]};
}
&:last-child {
margin-bottom: ${space[1]};
}
error-text-link
Defined in ErrorSummaryNotification/ErrorSummaryNotification.tsx
color: ${colors.error};
&:hover {
color: ${colors.errorAlt};
}
In brief
Notification is a message box that announces itself to a screen reader. An error is announced as an alert and every other variant as a status, and the dismiss button and actions are real buttons.
What Nebula provides
- The wrapper has the role alert for the error variant and status for the others, so new content is announced without moving focus.
- The wrapper can take focus from code through its ref, and shows a focus ring when it does.
- The icon is hidden from screen readers, so the variant is never announced by the icon alone.
- The dismiss button and each action are button elements with a visible focus ring. The dismiss button is named "Dismiss".
Keyboard behaviour
The notification itself is not a tab stop. The dismiss button and the actions are buttons, reached with Tab.
Focus order
- The dismiss button, when the notification is dismissible
- The actions, in the order they are passed
Interactions
- Tab - Moves focus to the dismiss button, then to each action in turn
- Enter or Space - Dismisses the notification or runs the focused action
Accessible design
Put the meaning in the text, not in the colour or the icon
The icon is hidden from screen readers and the colour is visual only, so a screen reader user hears the title and description and nothing else. Write them so they say what happened without the variant, for example "Meter read not submitted" and not just "Not submitted".
WCAG: 1.4.1 Use of Color 1.3.1 Info and Relationships
Avoid more than one dismissible notification in view
Every dismiss button is named "Dismiss", so a screen reader user cannot tell which message each one closes. Keep to one dismissible notification in the same place.
Accessible development
Always render the Notification before its content arrives
A live region is only announced when its content changes while it is already on the page. Render the Notification empty and pass the title or children when the message is ready: an empty Notification renders nothing visible but keeps its role.
WCAG: 4.1.3 Status Messages
If an error follows a form submit, move focus to the notification
Focus the notification through its ref so the user can read the error and act on it. For every other message, leave focus where it is: the role already announces it, and moving focus scrolls the page and shows a focus ring the user did not ask for. ErrorSummaryNotification moves focus to itself when errors arrive, so do not focus it again.
WCAG: 3.3.1 Error Identification
Avoid overriding the role
The defaults match the urgency of the message. An alert interrupts whatever the screen reader is saying, so do not set it on a message that is not an error.
WCAG: 4.1.3 Status Messages
Accessibility testing status
Reviewed on 9 October 2026 against v17.0.1.
| Test | Status | Tested with |
|---|---|---|
| Default state | Tested | axe-core via the Storybook test runner (CI) |
| Advanced states | Partially tested | axe-core via the Storybook test runner (CI) |
| Keyboard navigation | Partially tested | keyboard (reported by the owner) |
| Screen reader | Partially tested | VoiceOver with Chrome (reported by the owner) |
What these tests and statuses mean
The table records which checks were run on this component on its own. It is not a statement of WCAG conformance, and it does not cover how the component is used.
The tests
- Default state: The component as it first appears, before anything is clicked or changed.
- Advanced states: Every other state and variant: error, open, selected, each size and style, and hovering.
- Keyboard navigation: Whether every function can be reached and used with the keyboard alone, with a visible focus ring.
- Screen reader: Whether a screen reader announces what is on screen correctly: the name, the role and any state.
The statuses
- Tested: Every check in this row passed in an automated run that repeats on every pull request.
- Manually tested: Every check in this row was run once, by a person or with a tool they started, and all of them passed. It is not re-run on every change.
- Partially tested: Some checks in this row were run once and passed; the rest are still to do.
- Issue found: A check in this row failed. The ticket is shown beside the status.
- Not tested: No check in this row has been run yet.
- Not applicable: This row does not apply to the component, for the reason given beside it.