View as Markdown

Tooltip

Add Bootstrap tooltips to focusable controls with configurable content and placement.

100%
Loading...
<div class="d-flex flex-wrap gap-2">
    <twig:Tooltip title="Tooltip on left" placement="left">
        <button type="button" class="btn btn-secondary" {{ html_attr(tooltip_trigger_attrs) }}>Left</button>
    </twig:Tooltip>
    <twig:Tooltip title="Tooltip on top" placement="top">
        <button type="button" class="btn btn-secondary" {{ html_attr(tooltip_trigger_attrs) }}>Top</button>
    </twig:Tooltip>
    <twig:Tooltip title="Tooltip on bottom" placement="bottom">
        <button type="button" class="btn btn-secondary" {{ html_attr(tooltip_trigger_attrs) }}>Bottom</button>
    </twig:Tooltip>
    <twig:Tooltip title="Tooltip on right" placement="right">
        <button type="button" class="btn btn-secondary" {{ html_attr(tooltip_trigger_attrs) }}>Right</button>
    </twig:Tooltip>
</div>

<script type="module">
    import { Tooltip } from 'bootstrap';

    document.querySelectorAll('[data-bs-toggle="tooltip"]').forEach((element) => Tooltip.getOrCreateInstance(element));
</script>

Installation

php bin/console ux:install tooltip --kit bootstrap

Install the following Composer dependencies:

composer require twig/extra-bundle twig/html-extra:^3.24.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 title string The tooltip content stored by Bootstrap. #}
{# @prop placement &#039;auto&#039;|&#039;top&#039;|&#039;right&#039;|&#039;bottom&#039;|&#039;left&#039; The preferred tooltip placement. #}
{# @prop html boolean Whether the tooltip title contains sanitized HTML. #}
{# @prop customClass string|null The custom class applied to the generated tooltip. #}
{# @block content The focusable trigger rendering `tooltip_trigger_attrs` with `html_attr()`. #}
{%- props
    title,
    placement = &#039;top&#039;,
    html = false,
    customClass = null
-%}

{%- set final_placement = placement in [&#039;auto&#039;, &#039;top&#039;, &#039;right&#039;, &#039;bottom&#039;, &#039;left&#039;] ? placement : &#039;top&#039; -%}
{%- set tooltip_trigger_attrs = {
    &#039;data-bs-toggle&#039;: &#039;tooltip&#039;,
    &#039;data-bs-title&#039;: title,
    &#039;data-bs-placement&#039;: final_placement,
}|html_attr_merge -%}
{%- if html -%}
    {%- set tooltip_trigger_attrs = tooltip_trigger_attrs|merge({&#039;data-bs-html&#039;: &#039;true&#039;}) -%}
{%- endif -%}
{%- if customClass not in [null, &#039;&#039;] -%}
    {%- set tooltip_trigger_attrs = tooltip_trigger_attrs|merge({&#039;data-bs-custom-class&#039;: customClass}) -%}
{%- endif -%}

{%- block content %}{% endblock -%}

Usage

<twig:Tooltip title="Save your changes">
    <button type="button" class="btn btn-primary" {{ html_attr(tooltip_trigger_attrs) }}>Save</button>
</twig:Tooltip>

<script type="module">
    import { Tooltip } from 'bootstrap';

    document.querySelectorAll('[data-bs-toggle="tooltip"]').forEach((element) => Tooltip.getOrCreateInstance(element));
</script>

Accessibility

Apply tooltip_trigger_attrs to a natively focusable, interactive element such as a button or link so the tooltip works with both pointer and keyboard input. Do not configure hover as the only trigger.

Disabled controls cannot receive focus or pointer events. Put the tooltip attributes on a focusable wrapper as shown below. Bootstrap sanitizes HTML tooltip content by default, but plain text remains preferable for user-provided content.

Examples

Enable tooltips

Bootstrap tooltips are opt-in and must be initialized explicitly after Bootstrap's JavaScript is loaded.

100%
Loading...
<twig:Tooltip title="This tooltip is initialized explicitly">
    <button type="button" class="btn btn-secondary" {{ html_attr(tooltip_trigger_attrs) }}>Hover or focus me</button>
</twig:Tooltip>

<script type="module">
    import { Tooltip } from 'bootstrap';

    document.querySelectorAll('[data-bs-toggle="tooltip"]').forEach((element) => Tooltip.getOrCreateInstance(element));
</script>

Apply the trigger attributes directly to inline links so they remain keyboard accessible.

100%
Loading...
<p class="text-body-secondary mb-0">
    Placeholder text to demonstrate
    <twig:Tooltip title="Default tooltip"><a href="#" {{ html_attr(tooltip_trigger_attrs) }}>inline links</a></twig:Tooltip>
    with tooltips. Content placed here mimics how
    <twig:Tooltip title="Another tooltip"><a href="#" {{ html_attr(tooltip_trigger_attrs) }}>real text</a></twig:Tooltip>
    flows around them. You can use
    <twig:Tooltip title="Another one here too"><a href="#" {{ html_attr(tooltip_trigger_attrs) }}>these tooltips on links</a></twig:Tooltip>
    in your own interface.
</p>

<script type="module">
    import { Tooltip } from 'bootstrap';

    document.querySelectorAll('[data-bs-toggle="tooltip"]').forEach((element) => Tooltip.getOrCreateInstance(element));
</script>

Custom tooltips

Use a custom class and Bootstrap CSS variables to change a generated tooltip's appearance.

100%
Loading...
<style>
    .bootstrap-custom-tooltip {
        --bs-tooltip-bg: var(--bs-primary);
        --bs-tooltip-color: var(--bs-white);
    }
</style>

<twig:Tooltip title="This top tooltip is themed via CSS variables." customClass="bootstrap-custom-tooltip">
    <button type="button" class="btn btn-secondary" {{ html_attr(tooltip_trigger_attrs) }}>Custom tooltip</button>
</twig:Tooltip>

<script type="module">
    import { Tooltip } from 'bootstrap';

    document.querySelectorAll('[data-bs-toggle="tooltip"]').forEach((element) => Tooltip.getOrCreateInstance(element));
</script>

Directions

Choose a preferred placement or enable Bootstrap's sanitized HTML content.

100%
Loading...
<div class="d-flex flex-wrap gap-2">
    <twig:Tooltip title="Tooltip on top" placement="top">
        <button type="button" class="btn btn-secondary" {{ html_attr(tooltip_trigger_attrs) }}>Tooltip on top</button>
    </twig:Tooltip>
    <twig:Tooltip title="Tooltip on right" placement="right">
        <button type="button" class="btn btn-secondary" {{ html_attr(tooltip_trigger_attrs) }}>Tooltip on right</button>
    </twig:Tooltip>
    <twig:Tooltip title="Tooltip on bottom" placement="bottom">
        <button type="button" class="btn btn-secondary" {{ html_attr(tooltip_trigger_attrs) }}>Tooltip on bottom</button>
    </twig:Tooltip>
    <twig:Tooltip title="Tooltip on left" placement="left">
        <button type="button" class="btn btn-secondary" {{ html_attr(tooltip_trigger_attrs) }}>Tooltip on left</button>
    </twig:Tooltip>
    <twig:Tooltip title="<em>Tooltip</em> <u>with</u> <b>HTML</b>" html>
        <button type="button" class="btn btn-secondary" {{ html_attr(tooltip_trigger_attrs) }}>Tooltip with HTML</button>
    </twig:Tooltip>
</div>

<script type="module">
    import { Tooltip } from 'bootstrap';

    document.querySelectorAll('[data-bs-toggle="tooltip"]').forEach((element) => Tooltip.getOrCreateInstance(element));
</script>

Disabled elements

Put the trigger on a focusable wrapper when the described control is disabled.

100%
Loading...
<twig:Tooltip title="Disabled tooltip">
    <span class="d-inline-block" tabindex="0" {{ html_attr(tooltip_trigger_attrs) }}>
        <button class="btn btn-primary" type="button" disabled>Disabled button</button>
    </span>
</twig:Tooltip>

<script type="module">
    import { Tooltip } from 'bootstrap';

    document.querySelectorAll('[data-bs-toggle="tooltip"]').forEach((element) => Tooltip.getOrCreateInstance(element));
</script>

API Reference

<twig:Tooltip>

Prop Type Default
title 
string -
placement 
'auto'|'top'|'right'|'bottom'|'left' 'top'
html 
boolean false
customClass 
string|null null
Block Description
content The focusable trigger rendering tooltip_trigger_attrs with html_attr().