Toast
Show lightweight Bootstrap notifications with optional headers and dismissal controls.
<div class="d-flex justify-content-center p-4">
<twig:Toast :autohide="false">
<twig:Toast:Header timestamp="11 mins ago">
<span class="rounded me-2 bg-primary" style="width: 20px; height: 20px"></span>
<strong class="me-auto">Bootstrap</strong>
</twig:Toast:Header>
<twig:Toast:Body>
Hello, world! This is a toast message.
</twig:Toast:Body>
</twig:Toast>
</div>
Installation
php bin/console ux:install toast --kit bootstrap
Install the following Composer dependencies:
composer require twig/extra-bundle twig/html-extra:^3.12.0
Install the following front-end dependencies:
npm install bootstrap@^5.3.0
# ...or with Symfony AssetMapper
php bin/console importmap:require bootstrap bootstrap/dist/css/bootstrap.min.css
Copy the following file(s) into your app:
{# @prop show boolean Whether the toast is visible on initial render. #}
{# @prop autohide boolean Whether Bootstrap hides the toast automatically. #}
{# @prop delay int|null The delay in milliseconds before an autohiding toast closes. #}
{# @prop animation boolean Whether Bootstrap animates the toast. #}
{# @prop role 'status'|'alert' The accessibility role that describes the notification urgency. #}
{# @prop live 'polite'|'assertive' The live region politeness level. #}
{# @block content The toast structure, typically `Toast:Header` and `Toast:Body`. #}
{%- props
show = true,
autohide = true,
delay = null,
animation = true,
role = 'status',
live = 'polite'
-%}
{%- set final_role = role in ['status', 'alert'] ? role : 'status' -%}
{%- set final_live = live in ['polite', 'assertive'] ? live : 'polite' -%}
{%- set toast_classes = html_classes({
toast: true,
show: show,
}) -%}
{%- set default_attrs = {
class: toast_classes,
role: final_role,
'aria-live': final_live,
'aria-atomic': 'true',
} -%}
{%- if autohide and delay is not null -%}
{%- set default_attrs = default_attrs|merge({'data-bs-delay': delay}) -%}
{%- endif -%}
{%- if not autohide -%}
{%- set default_attrs = default_attrs|merge({'data-bs-autohide': 'false'}) -%}
{%- endif -%}
{%- if not animation -%}
{%- set default_attrs = default_attrs|merge({'data-bs-animation': 'false'}) -%}
{%- endif -%}
<div {{ attributes.defaults(default_attrs) }}>
{%- block content %}{% endblock -%}
</div>
{# @block content The notification message and optional actions. #}
<div {{ attributes.defaults({class: 'toast-body'}) }}>
{%- block content %}{% endblock -%}
</div>
{# @prop title string The notification title. #}
{# @prop timestamp string|null The optional relative or absolute notification time. #}
{# @prop imageSrc string|null The optional image displayed before the title. #}
{# @prop imageAlt string The alternative text for the header image. #}
{# @prop closeButton boolean Whether to render a Bootstrap dismiss button. #}
{# @prop closeLabel string The accessible label of the dismiss button. #}
{# @block content The header title or custom header content. #}
{%- props
title = '',
timestamp = null,
imageSrc = null,
imageAlt = '',
closeButton = true,
closeLabel = 'Close'
-%}
<div {{ attributes.defaults({class: 'toast-header'}) }}>
{%- if imageSrc is not null -%}
<img src="{{ imageSrc }}" class="rounded me-2" alt="{{ imageAlt }}">
{%- endif -%}
{%- block content -%}
<strong class="me-auto">{{ title }}</strong>
{%- endblock -%}
{%- if timestamp is not null -%}
<small class="text-body-secondary">{{ timestamp }}</small>
{%- endif -%}
{%- if closeButton -%}
<button type="button" class="btn-close" data-bs-dismiss="toast" aria-label="{{ closeLabel }}"></button>
{%- endif -%}
</div>
Usage
<twig:Toast :autohide="false">
<twig:Toast:Header title="Bootstrap" timestamp="just now" />
<twig:Toast:Body>
Your changes have been saved.
</twig:Toast:Body>
</twig:Toast>
Bootstrap toasts are opt-in. Create an instance before showing a hidden toast:
import { Toast } from 'bootstrap';
const trigger = document.querySelector('#live-toast-trigger');
const element = document.querySelector('#live-toast');
const toast = Toast.getOrCreateInstance(element);
trigger.addEventListener('click', () => toast.show());
A toast rendered with show is immediately visible and does not need JavaScript until it must hide, dismiss, or be shown again.
Accessibility
Keep the live region in the DOM before injecting or updating a toast. Use the defaults role="status" and live="polite" for ordinary notifications; reserve role="alert" and live="assertive" for urgent interruptions.
Choose a delay long enough to read the message. Set autohide to false when a toast contains interactive controls and always provide an explicit close action in that case. Avoid placing important actions inside an autohiding toast because focus may be lost when it disappears.
Examples
Basic
Compose a standard toast from a header and body.
<twig:Toast :autohide="false">
<twig:Toast:Header timestamp="11 mins ago">
<span class="rounded me-2 bg-primary" style="width: 20px; height: 20px"></span>
<strong class="me-auto">Bootstrap</strong>
</twig:Toast:Header>
<twig:Toast:Body>
Hello, world! This is a toast message.
</twig:Toast:Body>
</twig:Toast>
Live example
Start hidden and show the toast from application JavaScript.
<button type="button" class="btn btn-primary mb-3" id="live-toast-trigger">
Show live toast
</button>
<twig:Toast id="live-toast" :show="false">
<twig:Toast:Header title="Bootstrap" timestamp="just now" />
<twig:Toast:Body>
See? Just like this.
</twig:Toast:Body>
</twig:Toast>
Translucent
Place the default translucent toast over colored content.
<div class="p-4 rounded bg-primary bg-gradient">
<twig:Toast :autohide="false">
<twig:Toast:Header title="Bootstrap" timestamp="11 mins ago" />
<twig:Toast:Body>
Toasts are slightly translucent to blend with what is behind them.
</twig:Toast:Body>
</twig:Toast>
</div>
Stacking
Stack multiple notifications inside a Bootstrap toast container.
<div class="toast-container position-static">
<twig:Toast :autohide="false">
<twig:Toast:Header title="Bootstrap" timestamp="just now" />
<twig:Toast:Body>
First toast in the stack.
</twig:Toast:Body>
</twig:Toast>
<twig:Toast :autohide="false">
<twig:Toast:Header title="Bootstrap" timestamp="2 seconds ago" />
<twig:Toast:Body>
Second toast, automatically spaced by the container.
</twig:Toast:Body>
</twig:Toast>
</div>
Custom content
Omit the header and compose body content, actions, and dismissal controls directly.
<div class="d-flex flex-column gap-3">
<twig:Toast :autohide="false" class="align-items-center">
<div class="d-flex">
<twig:Toast:Body>
Hello, world! This is a toast message.
</twig:Toast:Body>
<button type="button" class="btn-close me-2 m-auto" data-bs-dismiss="toast" aria-label="Close"></button>
</div>
</twig:Toast>
<twig:Toast :autohide="false">
<twig:Toast:Body>
Hello, world! This is a toast message.
<div class="mt-2 pt-2 border-top">
<button type="button" class="btn btn-primary btn-sm">Take action</button>
<button type="button" class="btn btn-secondary btn-sm" data-bs-dismiss="toast">Close</button>
</div>
</twig:Toast:Body>
</twig:Toast>
</div>
Color schemes
Combine contextual background and foreground utilities for stronger visual emphasis.
<div class="d-flex flex-column gap-3">
{% for color in ['primary', 'success', 'danger'] %}
<twig:Toast :autohide="false" class="text-bg-{{ color }} border-0">
<div class="d-flex">
<twig:Toast:Body>
A {{ color }} toast with matching contextual color.
</twig:Toast:Body>
<button type="button" class="btn-close me-2 m-auto" data-bs-theme="dark" data-bs-dismiss="toast" aria-label="Close"></button>
</div>
</twig:Toast>
{% endfor %}
</div>
Placement
Position a toast container with Bootstrap's positioning utilities.
<div class="position-relative w-100 bg-body-tertiary border rounded" style="height: 320px">
<div class="toast-container position-absolute top-0 end-0 p-3">
<twig:Toast :autohide="false">
<twig:Toast:Header title="Bootstrap" timestamp="just now" />
<twig:Toast:Body>
Position the container with Bootstrap utilities.
</twig:Toast:Body>
</twig:Toast>
</div>
</div>
API Reference
<twig:Toast>
| Prop | Type | Default |
|---|---|---|
show Whether the toast is visible on initial render.
|
boolean |
true |
autohide Whether Bootstrap hides the toast automatically.
|
boolean |
true |
delay The delay in milliseconds before an autohiding toast closes.
|
int|null |
null |
animation Whether Bootstrap animates the toast.
|
boolean |
true |
role The accessibility role that describes the notification urgency.
|
'status'|'alert' |
'status' |
live The live region politeness level.
|
'polite'|'assertive' |
'polite' |
| Block | Description |
|---|---|
content |
The toast structure, typically Toast:Header and Toast:Body. |
<twig:Toast:Body>
| Block | Description |
|---|---|
content |
The notification message and optional actions. |
<twig:Toast:Header>
| Prop | Type | Default |
|---|---|---|
title The notification title.
|
string |
'' |
timestamp The optional relative or absolute notification time.
|
string|null |
null |
imageSrc The optional image displayed before the title.
|
string|null |
null |
imageAlt The alternative text for the header image.
|
string |
'' |
closeButton Whether to render a Bootstrap dismiss button.
|
boolean |
true |
closeLabel The accessible label of the dismiss button.
|
string |
'Close' |
| Block | Description |
|---|---|
content |
The header title or custom header content. |