Usage examples
Ta treść nie jest jeszcze dostępna w Twoim języku.
Each snippet renders in a fresh view, so widget id values start at w0. Long inline SVG icons are shortened to
<svg class="..." ...>...</svg>, and asset URLs are shown as /assets/<hash>/....
Widgets
Section titled “Widgets”Accordion
Section titled “Accordion”use yii2\extensions\bootstrap6\Accordion;
echo Accordion::widget( [ 'items' => [ ['label' => 'What is Yii?', 'content' => 'A high-performance PHP framework.', 'open' => true], 'Is it free?' => 'Yes, under the BSD-3-Clause license.', ], ],);<div id="w0" class="accordion"><details class="accordion-item" name="w0" open><summary class="accordion-header">What is Yii?<svg class="accordion-icon" ...>...</svg></summary><div class="accordion-body">A high-performance PHP framework.</div></details><details class="accordion-item" name="w0"><summary class="accordion-header">Is it free?<svg class="accordion-icon" ...>...</svg></summary><div class="accordion-body">Yes, under the BSD-3-Clause license.</div></details></div>The same accordion built with value objects renders the same markup:
use yii2\extensions\bootstrap6\Accordion;use yii2\extensions\bootstrap6\item\AccordionItem;
echo Accordion::widget( [ 'items' => [ AccordionItem::to('What is Yii?', 'A high-performance PHP framework.')->withOpen(true), AccordionItem::to('Is it free?', 'Yes, under the BSD-3-Clause license.'), ], ],);use yii2\extensions\bootstrap6\Alert;use yii2\extensions\bootstrap6\enum\Theme;
echo Alert::widget(['body' => 'Profile saved.', 'heading' => 'Done', 'theme' => Theme::SUCCESS]);<div id="w0" class="alert theme-success" role="alert"><div class="vstack"><h4 class="alert-heading mb-2">Done</h4>Profile saved.</div><button type="button" class="btn-close ms-auto" data-bs-dismiss="alert" aria-label="Close"></button></div>Content between begin() and end() is not encoded:
Alert::begin(['theme' => Theme::WARNING, 'closeButton' => false]);echo 'Check your <strong>input</strong>.';Alert::end();<div id="w0" class="alert theme-warning" role="alert">Check your <strong>input</strong>.</div>Avatar
Section titled “Avatar”use yii2\extensions\bootstrap6\Avatar;use yii2\extensions\bootstrap6\enum\{Size, Theme};
echo Avatar::widget(['initials' => 'WA', 'size' => Size::LG, 'theme' => Theme::PRIMARY, 'status' => 'online']);echo Avatar::widget(['src' => '/img/user.png', 'alt' => 'Wilmer']);<span id="w0" class="avatar avatar-lg theme-primary">WA<span class="avatar-status status-online" role="img" aria-label="Online"></span></span><span id="w1" class="avatar"><img class="avatar-img" src="/img/user.png" alt="Wilmer"></span>use yii2\extensions\bootstrap6\Badge;use yii2\extensions\bootstrap6\enum\Theme;
echo Badge::widget(['label' => 'New', 'theme' => Theme::SUCCESS, 'pill' => true]);<span id="w0" class="badge rounded-pill theme-success">New</span>Breadcrumbs
Section titled “Breadcrumbs”use yii2\extensions\bootstrap6\Breadcrumbs;
echo Breadcrumbs::widget(['links' => [['label' => 'Library', 'url' => ['/library/index']], 'Data']]);<nav aria-label="breadcrumb"><ol id="w0" class="breadcrumb"><li class="breadcrumb-item"><a class="breadcrumb-link" href="/index.php">Home</a></li><li class="breadcrumb-divider"></li><li class="breadcrumb-item"><a class="breadcrumb-link" href="/index.php?r=library%2Findex">Library</a></li><li class="breadcrumb-divider"></li><li class="breadcrumb-item" aria-current="page"><a class="breadcrumb-link active">Data</a></li></ol></nav>The same trail built with value objects renders the same markup:
use yii2\extensions\bootstrap6\Breadcrumbs;use yii2\extensions\bootstrap6\item\BreadcrumbLink;
echo Breadcrumbs::widget( ['links' => [BreadcrumbLink::to('Library', ['/library/index']), BreadcrumbLink::to('Data')]],);Button
Section titled “Button”use yii2\extensions\bootstrap6\Button;use yii2\extensions\bootstrap6\enum\{ButtonVariant, Size, Theme};
echo Button::widget(['label' => 'Save', 'theme' => Theme::PRIMARY]);echo Button::widget(['label' => 'Bold', 'variant' => ButtonVariant::OUTLINE, 'size' => Size::SM, 'toggle' => true]);<button type="button" id="w0" class="btn-solid theme-primary">Save</button><button type="button" id="w1" class="btn-outline btn-sm" data-bs-toggle="button" aria-pressed="false">Bold</button>ButtonGroup
Section titled “ButtonGroup”use yii2\extensions\bootstrap6\ButtonGroup;
echo ButtonGroup::widget( [ 'buttons' => [['label' => 'Left'], ['label' => 'Middle'], ['label' => 'Right']], 'options' => ['aria-label' => 'Alignment'], ],);<div id="w0" class="btn-group" aria-label="Alignment" role="group"><button type="button" id="w1" class="btn-solid">Left</button><button type="button" id="w2" class="btn-solid">Middle</button><button type="button" id="w3" class="btn-solid">Right</button></div>ButtonMenu
Section titled “ButtonMenu”A split button renders the action button and a separate toggle inside .btn-group:
ButtonToolbar
Section titled “ButtonToolbar”use yii2\extensions\bootstrap6\ButtonToolbar;
echo ButtonToolbar::widget( [ 'buttonGroups' => [ ['buttons' => [['label' => '1'], ['label' => '2']], 'options' => ['class' => 'me-2']], ['buttons' => [['label' => '3']]], ], ],);<div id="w0" class="btn-toolbar" role="toolbar"><div id="w1" class="me-2 btn-group" role="group"><button type="button" id="w2" class="btn-solid">1</button><button type="button" id="w3" class="btn-solid">2</button></div><div id="w4" class="btn-group" role="group"><button type="button" id="w5" class="btn-solid">3</button></div></div>Carousel
Section titled “Carousel”use yii2\extensions\bootstrap6\Carousel;
echo Carousel::widget( [ 'autoplay' => true, 'playPause' => true, 'items' => [ '<img src="/img/1.jpg" class="d-block w-100" alt="First">', ['content' => '<img src="/img/2.jpg" class="d-block w-100" alt="Second">'], ], ],);<div id="w0" class="carousel slide" data-bs-autoplay="true"><div class="carousel-inner"><div class="carousel-item active"><img src="/img/1.jpg" class="d-block w-100" alt="First"></div><div class="carousel-item"><img src="/img/2.jpg" class="d-block w-100" alt="Second"></div></div><div class="d-flex justify-content-between align-items-center"><div><button type="button" class="btn-icon btn-sm carousel-control-play-pause" data-bs-target="#w0" data-bs-pause-label="Pause" data-bs-play-label="Play" aria-label="Pause"><span class="carousel-icon-pause" aria-hidden="true"></span><span class="carousel-icon-play" aria-hidden="true"></span></button><button type="button" class="btn-icon btn-sm" data-bs-target="#w0" data-bs-slide="prev"><span class="carousel-icon-prev" aria-hidden="true"></span><span class="visually-hidden">Previous</span></button><button type="button" class="btn-icon btn-sm" data-bs-target="#w0" data-bs-slide="next"><span class="carousel-icon-next" aria-hidden="true"></span><span class="visually-hidden">Next</span></button></div><div class="carousel-indicators"><button type="button" class="active" data-bs-target="#w0" data-bs-slide-to="0" aria-current="true" aria-label="Slide 1"></button><button type="button" data-bs-target="#w0" data-bs-slide-to="1" aria-label="Slide 2"></button></div></div></div>The plugin is always created:
import("/assets/<hash>/bootstrap.bundle.js").then((bootstrap) => { bootstrap.Carousel.getOrCreateInstance(document.getElementById("w0"), {}); });The same slides as value objects render the same markup:
use yii2\extensions\bootstrap6\Carousel;use yii2\extensions\bootstrap6\item\CarouselItem;
echo Carousel::widget( [ 'autoplay' => true, 'playPause' => true, 'items' => [ CarouselItem::to('<img src="/img/1.jpg" class="d-block w-100" alt="First">'), CarouselItem::to('<img src="/img/2.jpg" class="d-block w-100" alt="Second">'), ], ],);use yii2\extensions\bootstrap6\Chips;
echo Chips::widget(['name' => 'tags', 'value' => ['php', 'yii'], 'placeholder' => 'Add a tag']);<div id="w0-chips" class="chip-input" data-bs-chips><span class="chip">php</span><span class="chip">yii</span><input type="text" id="w0" class="form-ghost" placeholder="Add a tag"><input type="hidden" id="w0-value" name="tags" value="php,yii"></div>The widget keeps the hidden input in sync with the plugin:
document.getElementById("w0-chips").addEventListener("change.bs.chips", (event) => { document.getElementById("w0-value").value = event.values.join(","); });Collapse
Section titled “Collapse”use yii2\extensions\bootstrap6\Collapse;
Collapse::begin(['toggleButton' => ['label' => 'Details']]);echo 'Hidden content.';Collapse::end();<button type="button" class="btn-solid theme-primary" data-bs-toggle="collapse" data-bs-target="#w0" aria-expanded="false" aria-controls="w0">Details</button><div id="w0" class="collapse">Hidden content.</div>The fluent methods chain on the value returned by begin(). A toggler method enables the toggle button, and a class
added to it replaces the default btn-solid theme-primary:
Collapse::begin() ->addClass('border-bottom') ->addCssStyle(['max-width' => '30rem']) ->addTogglerClass('btn-outline', 'theme-secondary');echo 'Hidden content.';Collapse::end();<button type="button" class="btn-outline theme-secondary" data-bs-toggle="collapse" data-bs-target="#w0" aria-expanded="false" aria-controls="w0">Show</button><div id="w0" class="border-bottom collapse" style="max-width: 30rem;">Hidden content.</div>Combobox
Section titled “Combobox”use yii2\extensions\bootstrap6\Combobox;
echo Combobox::widget( [ 'name' => 'fruit', 'items' => ['apple' => 'Apple', 'pear' => 'Pear'], 'placeholder' => 'Choose a fruit', 'value' => 'pear', ],);<button type="button" id="w0" class="form-control combobox-toggle" data-bs-toggle="combobox" data-bs-name="fruit" data-bs-placeholder="Choose a fruit"><span class="combobox-value">Pear</span><svg class="combobox-caret" ...>...</svg></button><div class="menu"><button type="button" class="menu-item" data-bs-value="apple">Apple</button><button type="button" class="menu-item selected" data-bs-value="pear" aria-selected="true">Pear</button></div>multiple, search, and check add data-bs-multiple="true", data-bs-search="true", the .combobox-search
field, the .combobox-no-results element, and a .menu-item-check icon in every item.
Datepicker
Section titled “Datepicker”use yii2\extensions\bootstrap6\Datepicker;
echo Datepicker::widget(['name' => 'date', 'locale' => 'en-US', 'dateMin' => '2026-01-01']);<input type="text" id="w0" class="form-control" name="date" autocomplete="off" data-bs-toggle="datepicker" data-bs-date-min="2026-01-01" data-bs-locale="en-US">In input mode the field submits the locale-formatted text shown to the user, so parse it with the same locale on the
server. Inline mode keeps the YYYY-MM-DD values in a hidden input:
echo Datepicker::widget( [ 'name' => 'range', 'inline' => true, 'selectionMode' => 'multiple-ranged', 'value' => '2026-10-01,2026-10-05', ],);<div id="w0-datepicker" data-bs-toggle="datepicker" data-bs-inline="true" data-bs-selection-mode="multiple-ranged" data-bs-selected-dates='["2026-10-01","2026-10-05"]' data-bs-position-element="#w0-datepicker-calendar"><input type="hidden" id="w0" name="range" value="2026-10-01,2026-10-05"><div id="w0-datepicker-calendar"></div></div>Dialog
Section titled “Dialog”use yii2\extensions\bootstrap6\Dialog;
Dialog::begin( [ 'title' => 'Delete post', 'toggleButton' => ['label' => 'Delete'], 'footer' => '<button type="button" class="btn-solid theme-danger">Delete</button>', ],);echo '<p>This action cannot be undone.</p>';Dialog::end();<button type="button" class="btn-solid theme-primary" data-bs-toggle="dialog" data-bs-target="#w0">Delete</button><dialog id="w0" class="dialog" aria-labelledby="w0-label"><div class="dialog-header"><h1 id="w0-label" class="dialog-title">Delete post</h1><button type="button" class="btn-close" data-bs-dismiss="dialog" aria-label="Close"></button></div><div class="dialog-body"><p>This action cannot be undone.</p></div><div class="dialog-footer"><button type="button" class="btn-solid theme-danger">Delete</button></div></dialog>The same dialog configured with the fluent methods, which chain on the value returned by begin(); the dialog class
is still added while rendering:
Dialog::begin(['title' => 'Delete post', 'toggleButton' => ['label' => 'Delete']]) ->addClass('shadow-lg') ->addTogglerClass('btn-outline', 'theme-danger') ->addTogglerAttribute('aria-haspopup', 'dialog');echo '<p>This action cannot be undone.</p>';Dialog::end();<button type="button" class="btn-outline theme-danger" aria-haspopup="dialog" data-bs-toggle="dialog" data-bs-target="#w0">Delete</button><dialog id="w0" class="shadow-lg dialog" aria-labelledby="w0-label"><div class="dialog-header"><h1 id="w0-label" class="dialog-title">Delete post</h1><button type="button" class="btn-close" data-bs-dismiss="dialog" aria-label="Close"></button></div><div class="dialog-body"><p>This action cannot be undone.</p></div></dialog>Sizes, responsive fullscreen, and backdrop behavior:
use yii2\extensions\bootstrap6\enum\{Breakpoint, DialogSize};
Dialog::begin( [ 'title' => 'Terms of service', 'toggleButton' => ['label' => 'Read the terms'], 'size' => DialogSize::LG, 'fullscreenBelow' => Breakpoint::MD, 'backdrop' => 'static', 'scrollable' => true, ],);echo '<p>A static backdrop keeps the dialog open on outside clicks; close it with the button.</p>';echo '<p>The body scrolls inside the dialog when the content is taller than the viewport.</p>';Dialog::end();<button type="button" class="btn-solid theme-primary" data-bs-toggle="dialog" data-bs-target="#w0">Read the terms</button><dialog id="w0" class="dialog dialog-lg md-down:dialog-fullscreen dialog-scrollable" aria-labelledby="w0-label" data-bs-backdrop="static"><div class="dialog-header"><h1 id="w0-label" class="dialog-title">Terms of service</h1><button type="button" class="btn-close" data-bs-dismiss="dialog" aria-label="Close"></button></div><div class="dialog-body"><p>A static backdrop keeps the dialog open on outside clicks; close it with the button.</p><p>The body scrolls inside the dialog when the content is taller than the viewport.</p></div></dialog>Drawer
Section titled “Drawer”use yii2\extensions\bootstrap6\Drawer;use yii2\extensions\bootstrap6\enum\DrawerPlacement;
Drawer::begin( ['title' => 'Filters', 'placement' => DrawerPlacement::END, 'toggleButton' => ['label' => 'Show filters']],);echo 'Filter form';Drawer::end();<button type="button" class="btn-solid theme-primary" data-bs-toggle="drawer" data-bs-target="#w0" aria-controls="w0">Show filters</button><dialog id="w0" class="drawer drawer-end" tabindex="-1" aria-labelledby="w0-label"><div class="drawer-header"><h5 id="w0-label" class="drawer-title">Filters</h5><button type="button" class="btn-close" data-bs-dismiss="drawer" aria-label="Close"></button></div><div class="drawer-body">Filter form</div></dialog>'responsive' => Breakpoint::LG renders class="lg:drawer drawer-start": the content is a drawer below lg and
inline from lg up.
LinkPager
Section titled “LinkPager”use yii\data\Pagination;use yii2\extensions\bootstrap6\LinkPager;use yii2\extensions\bootstrap6\enum\{Size, Theme};
echo LinkPager::widget( [ 'pagination' => new Pagination(['totalCount' => 30, 'pageSize' => 10, 'page' => 1, 'route' => 'post/index']), 'size' => Size::SM, 'theme' => Theme::PRIMARY, ],);<nav id="w0"><ul class="pagination pagination-sm theme-primary"><li class="page-item prev"><a class="page-link" href="/index.php?r=post%2Findex&page=1&per-page=10" data-page="0" aria-label="Previous"><span aria-hidden="true">«</span></a></li><li class="page-item"><a class="page-link" href="/index.php?r=post%2Findex&page=1&per-page=10" data-page="0">1</a></li><li class="page-item active"><a class="page-link" href="/index.php?r=post%2Findex&page=2&per-page=10" data-page="1" aria-current="page">2</a></li><li class="page-item"><a class="page-link" href="/index.php?r=post%2Findex&page=3&per-page=10" data-page="2">3</a></li><li class="page-item next"><a class="page-link" href="/index.php?r=post%2Findex&page=3&per-page=10" data-page="2" aria-label="Next"><span aria-hidden="true">»</span></a></li></ul></nav>Use it in every grid and list view through the DI container: add the definition to container.definitions in
config/web.php, or set it at runtime:
use yii\data\ArrayDataProvider;use yii\widgets\ListView;
// config/web.php: 'container' => ['definitions' => [\yii\widgets\LinkPager::class => LinkPager::class]]Yii::$container->set(\yii\widgets\LinkPager::class, LinkPager::class);
echo ListView::widget( [ 'dataProvider' => new ArrayDataProvider( ['allModels' => range(1, 30), 'pagination' => ['pageSize' => 10, 'route' => 'post/index']], ), 'itemView' => static fn(int $number): string => "Post $number", 'layout' => '{pager}', ],);Menu renders only the panel; place it after your own toggle:
The same panel built with value objects renders the same markup:
The Menu plugin binds to the toggle, so clientOptions and clientEvents set on a standalone Menu target the panel
and have no effect on the toggle. Put the options on the toggle as data-bs-* attributes, or use ButtonMenu.
The same nav built with value objects renders the same markup; leave active as null to detect it from the route:
NavBar
Section titled “NavBar”'drawerOptions' => false renders the content inline without toggler and drawer, and 'expand' => null renders
navbar-expand for every width.
NavOverflow
Section titled “NavOverflow”OtpInput
Section titled “OtpInput”use yii2\extensions\bootstrap6\OtpInput;
echo OtpInput::widget(['name' => 'code', 'length' => 6, 'groups' => [3, 3]]);<div id="w0-otp" class="otp" data-bs-otp data-bs-groups='[3,3]'><input type="text" id="w0" class="otp-input" name="code" maxlength="6"></div>Placeholder
Section titled “Placeholder”use yii2\extensions\bootstrap6\Placeholder;use yii2\extensions\bootstrap6\enum\Size;
echo Placeholder::widget(['width' => 6, 'animation' => 'wave']);echo Placeholder::widget(['width' => 8, 'animation' => 'pulse', 'container' => true, 'size' => Size::LG]);<span id="w0" class="placeholder col-6 placeholder-wave"></span><p class="placeholder-pulse"><span id="w1" class="placeholder col-8 placeholder-lg"></span></p>Popover
Section titled “Popover”use yii2\extensions\bootstrap6\Popover;use yii2\extensions\bootstrap6\enum\Placement;
Popover::begin(['title' => 'Popover title', 'content' => 'And here is some content.', 'placement' => Placement::RIGHT]);echo '<button type="button" class="btn-solid theme-danger">Click me</button>';Popover::end();<span id="w0" data-bs-toggle="popover" data-bs-title="Popover title" data-bs-placement="right" data-bs-content="And here is some content."><button type="button" class="btn-solid theme-danger">Click me</button></span>Progress
Section titled “Progress”use yii2\extensions\bootstrap6\Progress;use yii2\extensions\bootstrap6\enum\Theme;
echo Progress::widget( ['percent' => 60, 'label' => '60%', 'theme' => Theme::SUCCESS, 'striped' => true, 'ariaLabel' => 'Upload'],);<div id="w0" class="progress" role="progressbar" aria-label="Upload" aria-valuenow="60" aria-valuemin="0" aria-valuemax="100"><div class="progress-bar theme-success progress-bar-striped" style="width: 60%;">60%</div></div>Stacked segments size the .progress elements:
echo Progress::widget( [ 'bars' => [ ['percent' => 30, 'theme' => Theme::PRIMARY, 'ariaLabel' => 'Segment one'], ['percent' => 20, 'theme' => Theme::WARNING, 'ariaLabel' => 'Segment two'], ], ],);<div id="w0" class="progress-stacked"><div class="progress" role="progressbar" aria-label="Segment one" aria-valuenow="30" aria-valuemin="0" aria-valuemax="100" style="width: 30%;"><div class="progress-bar theme-primary"></div></div><div class="progress" role="progressbar" aria-label="Segment two" aria-valuenow="20" aria-valuemin="0" aria-valuemax="100" style="width: 20%;"><div class="progress-bar theme-warning"></div></div></div>The same segments as value objects render the same markup:
use yii2\extensions\bootstrap6\item\ProgressBar;
echo Progress::widget( [ 'bars' => [ ProgressBar::to(30)->withTheme(Theme::PRIMARY)->withAriaLabel('Segment one'), ProgressBar::to(20)->withTheme(Theme::WARNING)->withAriaLabel('Segment two'), ], ],);use yii2\extensions\bootstrap6\Range;
echo Range::widget( [ 'name' => 'volume', 'value' => 40, 'min' => 0, 'max' => 100, 'step' => 10, 'bubble' => true, 'ticks' => [0 => 'Mute', 50 => null, 100 => 'Max'], ],);<div id="w0-range" class="form-range" data-bs-bubble><input type="range" id="w0" class="form-range-input" name="volume" value="40" min="0" max="100" step="10" list="w0-ticks"></div><datalist id="w0-ticks"><option value="0" label="Mute"></option><option value="50"></option><option value="100" label="Max"></option></datalist>Spinner
Section titled “Spinner”use yii2\extensions\bootstrap6\Spinner;use yii2\extensions\bootstrap6\enum\Theme;
echo Spinner::widget(['theme' => Theme::PRIMARY]);echo Spinner::widget(['type' => Spinner::TYPE_GROW, 'small' => true, 'label' => '']);<div id="w0" class="spinner-border fg-primary" role="status"><span class="visually-hidden">Loading...</span></div><div id="w1" class="spinner-grow spinner-sm" role="status"></div>Stepper
Section titled “Stepper”use yii2\extensions\bootstrap6\Stepper;use yii2\extensions\bootstrap6\enum\Breakpoint;
echo Stepper::widget( [ 'items' => [ ['label' => 'Cart', 'active' => true], ['label' => 'Shipping', 'active' => true], ['label' => 'Payment'], ], ],);<ol id="w0" class="stepper"><li class="stepper-item active">Cart</li><li class="stepper-item active">Shipping</li><li class="stepper-item">Payment</li></ol>A responsive horizontal stepper with a description and a link:
echo Stepper::widget( [ 'horizontal' => Breakpoint::MD, 'items' => [ ['label' => 'Account', 'description' => 'Create your login', 'active' => true], ['label' => 'Profile', 'url' => ['/profile']], ], ],);<div class="contains-inline"><div id="w0" class="stepper md:stepper-horizontal"><div class="stepper-item align-items-start active"><div class="pt-1"><div class="fw-semibold">Account</div><small class="fg-3">Create your login</small></div></div><a class="stepper-item" href="/index.php?r=profile">Profile</a></div></div>The same stepper built with value objects renders the same markup:
use yii2\extensions\bootstrap6\item\StepItem;
echo Stepper::widget( [ 'horizontal' => Breakpoint::MD, 'items' => [ StepItem::to('Account', 'Create your login')->withActive(true), StepItem::to('Profile')->withUrl(['/profile']), ], ],);Strength
Section titled “Strength”use yii2\extensions\bootstrap6\{Html, Strength};
echo Html::passwordInput('password', null, ['class' => 'form-control']);echo Strength::widget(['showText' => true, 'messages' => ['weak' => 'Too weak', 'strong' => 'Great']]);<input type="password" class="form-control" name="password"><div id="w0" class="strength" data-bs-strength><div class="strength-segment"></div><div class="strength-segment"></div><div class="strength-segment"></div><div class="strength-segment"></div></div><span class="strength-text"></span>messages are passed to the plugin constructor:
import("/assets/<hash>/bootstrap.bundle.js").then((bootstrap) => { bootstrap.Strength.getOrCreateInstance(document.getElementById("w0"), {"messages":{"weak":"Too weak","strong":"Great"}}); });use yii2\extensions\bootstrap6\Tabs;
echo Tabs::widget( [ 'items' => [ ['label' => 'Home', 'content' => 'Home pane.'], ['label' => 'Profile', 'content' => 'Profile pane.'], ['label' => 'Disabled', 'disabled' => true], ], ],);<ul id="w0" class="nav nav-tabs" role="tablist"><li class="nav-item" role="presentation"><button type="button" id="w0-tab0" class="nav-link active" data-bs-toggle="tab" data-bs-target="#w0-pane0" role="tab" aria-controls="w0-pane0" aria-selected="true">Home</button></li><li class="nav-item" role="presentation"><button type="button" id="w0-tab1" class="nav-link" data-bs-toggle="tab" data-bs-target="#w0-pane1" role="tab" aria-controls="w0-pane1" aria-selected="false">Profile</button></li><li class="nav-item" role="presentation"><button type="button" id="w0-tab2" class="nav-link" disabled data-bs-toggle="tab" data-bs-target="#w0-pane2" role="tab" aria-controls="w0-pane2" aria-selected="false">Disabled</button></li></ul><div class="tab-content"><div id="w0-pane0" class="tab-pane active" role="tabpanel" aria-labelledby="w0-tab0" tabindex="0">Home pane.</div><div id="w0-pane1" class="tab-pane" role="tabpanel" aria-labelledby="w0-tab1" tabindex="0">Profile pane.</div><div id="w0-pane2" class="tab-pane" role="tabpanel" aria-labelledby="w0-tab2" tabindex="0"></div></div>The same tabs built with value objects render the same markup:
use yii2\extensions\bootstrap6\item\TabItem;use yii2\extensions\bootstrap6\Tabs;
echo Tabs::widget( [ 'items' => [ TabItem::to('Home', 'Home pane.'), TabItem::to('Profile', 'Profile pane.'), TabItem::to('Disabled')->withDisabled(true), ], ],);use yii2\extensions\bootstrap6\Toast;
echo Toast::widget(['title' => 'Bootstrap', 'body' => 'Hello, world! This is a toast message.']);<div id="w0" class="toast" role="alert" aria-live="assertive" aria-atomic="true"><div class="toast-header"><strong class="me-auto">Bootstrap</strong><button type="button" class="btn-close" data-bs-dismiss="toast" aria-label="Close"></button></div><div class="toast-body">Hello, world! This is a toast message.</div></div>The widget creates the plugin instance but does not show the toast. Show it from the view with the bundle helper:
use yii2\extensions\bootstrap6\asset\BootstrapPluginAsset;
$bundle = BootstrapPluginAsset::register($this);
$this->registerJs( $bundle->importJs($this, 'bootstrap.Toast.getOrCreateInstance(document.getElementById("w0")).show();'),);Set dateTime to render a relative time such as <small>2 minutes ago</small> in the header.
ToggleButtonGroup
Section titled “ToggleButtonGroup”use yii2\extensions\bootstrap6\ToggleButtonGroup;use yii2\extensions\bootstrap6\enum\Theme;
echo ToggleButtonGroup::widget( [ 'name' => 'style', 'type' => ToggleButtonGroup::TYPE_RADIO, 'items' => ['b' => 'Bold', 'i' => 'Italic'], 'value' => 'b', 'theme' => Theme::PRIMARY, ],);<div id="w0" class="btn-group" role="group"><label class="btn-check btn-outline theme-primary"><input type="radio" name="style" value="b" checked autocomplete="off">Bold</label><label class="btn-check btn-outline theme-primary"><input type="radio" name="style" value="i" autocomplete="off">Italic</label></div>Toggler
Section titled “Toggler”use yii2\extensions\bootstrap6\Toggler;
echo Toggler::widget(['label' => 'Toggle highlight', 'value' => 'theme-primary']);echo Toggler::widget(['label' => 'Show details', 'target' => '#details']);<button type="button" id="w0" data-bs-toggle="toggler" data-bs-value="theme-primary" data-bs-attribute="class">Toggle highlight</button><button type="button" id="w1" data-bs-toggle="toggler" data-bs-target="#details">Show details</button>With target, the matching elements carry their own data-bs-value and data-bs-attribute attributes.
Tooltip
Section titled “Tooltip”use yii2\extensions\bootstrap6\Tooltip;use yii2\extensions\bootstrap6\enum\Placement;
Tooltip::begin(['title' => 'Copied!', 'placement' => Placement::TOP]);echo 'Hover me';Tooltip::end();<span id="w0" data-bs-toggle="tooltip" data-bs-title="Copied!" data-bs-placement="top">Hover me</span>The examples use this model, validated with an invalid email so the forms show the error state; the CSRF input
rendered after <form> is omitted from the output.
use yii\base\Model;use yii2\extensions\bootstrap6\Html;
final class SignupForm extends Model{ public $email; public $password; public $remember = true; public $notify; public $topics = ['php']; public $plan = 'pro'; public $country; public $volume = 50; public $username = 'admin'; public $amount; public $tags; public $fruit; public $birthday;
public function attributeHints(): array { return ['password' => 'At least 8 characters.']; }
public function rules(): array { return [[['email', 'password'], 'required'], ['email', 'email']]; }}
$model = new SignupForm(['email' => 'not-an-email']);$model->validate(['email']);
echo Html::errorSummary($model, ['class' => 'alert theme-danger']);<div class="alert theme-danger"><p>Please fix the following errors:</p><ul><li>Email is not a valid email address.</li></ul></div>Default layout
Section titled “Default layout”use yii2\extensions\bootstrap6\ActiveForm;
$form = ActiveForm::begin(['action' => '/signup']);echo $form->field($model, 'email')->textInput(['type' => 'email']);echo $form->field($model, 'password')->passwordInput();echo $form->field($model, 'remember')->checkbox();ActiveForm::end();<form id="w0" action="/signup" method="post"><div class="form-field mb-3 field-signupform-email required"><label class="form-label" for="signupform-email">Email</label><input type="email" id="signupform-email" class="form-control is-invalid" name="SignupForm[email]" value="not-an-email" aria-required="true" aria-invalid="true">
<div class="invalid-feedback">Email is not a valid email address.</div></div><div class="form-field mb-3 field-signupform-password required"><label class="form-label" for="signupform-password">Password</label><input type="password" id="signupform-password" class="form-control" name="SignupForm[password]" aria-required="true"><div class="form-text">At least 8 characters.</div><div class="invalid-feedback"></div></div><div class="form-field mb-3 field-signupform-remember"><input type="hidden" name="SignupForm[remember]" value="0"><input type="checkbox" id="signupform-remember" class="check" name="SignupForm[remember]" value="1" checked><label for="signupform-remember">Remember</label>
<div class="invalid-feedback"></div></div></form>Horizontal layout
Section titled “Horizontal layout”$form = ActiveForm::begin(['action' => '/signup', 'layout' => ActiveForm::LAYOUT_HORIZONTAL]);echo $form->field($model, 'email');echo $form->field($model, 'remember')->checkbox();ActiveForm::end();With an error on email:
<div class="row mb-3 field-signupform-email required"><label class="sm:col-2 col-form-label" for="signupform-email">Email</label><div class="sm:col-10"><input type="text" id="signupform-email" class="form-control is-invalid" name="SignupForm[email]" value="not-an-email" aria-required="true" aria-invalid="true">
<div class="invalid-feedback">Email is not a valid email address.</div></div></div><div class="row mb-3 field-signupform-remember"><div class="sm:col-10 sm:offset-2"><div class="form-field"><input type="hidden" name="SignupForm[remember]" value="0"><input type="checkbox" id="signupform-remember" class="check" name="SignupForm[remember]" value="1" checked><label for="signupform-remember">Remember</label>
<div class="invalid-feedback"></div></div></div></div>Change the grid through fieldConfig:
$form = ActiveForm::begin( [ 'action' => '/signup', 'layout' => ActiveForm::LAYOUT_HORIZONTAL, 'fieldConfig' => [ 'horizontalCssClasses' => [ 'label' => 'md:col-3 col-form-label', 'offset' => 'md:col-9 md:offset-3', 'wrapper' => 'md:col-9', ], ], ],);echo $form->field($model, 'username');ActiveForm::end();<div class="row mb-3 field-signupform-username"><label class="md:col-3 col-form-label" for="signupform-username">Username</label><div class="md:col-9"><input type="text" id="signupform-username" class="form-control" name="SignupForm[username]" value="admin">
<div class="invalid-feedback"></div></div></div>Floating layout
Section titled “Floating layout”The label comes before the control inside .form-floating; the hint and the error follow the wrapper. The wrapper
receives is-invalid on the server so the sibling feedback is displayed. On the client, a :has() rule registered
once per form displays the feedback when the control carries is-invalid, so client validation needs no jQuery:
$form = ActiveForm::begin(['action' => '/signup', 'layout' => ActiveForm::LAYOUT_FLOATING]);echo $form->field($model, 'email');ActiveForm::end();<div class="form-field mb-3 field-signupform-email required"><div class="form-floating is-invalid"><label for="signupform-email">Email</label><input type="text" id="signupform-email" class="form-control is-invalid" name="SignupForm[email]" value="not-an-email" placeholder="Email" aria-required="true" aria-invalid="true"></div>
<div class="invalid-feedback">Email is not a valid email address.</div></div>Inline layout
Section titled “Inline layout”$form = ActiveForm::begin(['action' => '/signup', 'layout' => ActiveForm::LAYOUT_INLINE]);echo $form->field($model, 'email');echo $form->field($model, 'remember')->checkbox();ActiveForm::end();<form id="w0" class="row lg:row-cols-auto g-3 align-items-center" action="/signup" method="post"><div class="col-12 field-signupform-email required"><label class="visually-hidden" for="signupform-email">Email</label><input type="text" id="signupform-email" class="form-control is-invalid" name="SignupForm[email]" value="not-an-email" placeholder="Email" aria-required="true" aria-invalid="true">
</div><div class="col-12 field-signupform-remember"><div class="form-field"><input type="hidden" name="SignupForm[remember]" value="0"><input type="checkbox" id="signupform-remember" class="check" name="SignupForm[remember]" value="1" checked><label for="signupform-remember">Remember</label>
</div></div></form>Switch
Section titled “Switch”echo $form->field($model, 'notify')->checkbox(['switch' => true, 'switchOptions' => ['class' => 'theme-success']]);<div class="form-field mb-3 field-signupform-notify"><input type="hidden" name="SignupForm[notify]" value="0"><div class="switch theme-success"><input type="checkbox" id="signupform-notify" name="SignupForm[notify]" value="1" role="switch" switch></div><label for="signupform-notify">Notify</label>
<div class="invalid-feedback"></div></div>Checkbox and radio lists
Section titled “Checkbox and radio lists”echo $form->field($model, 'topics')->inline()->checkboxList(['php' => 'PHP', 'js' => 'JavaScript']);echo $form->field($model, 'plan')->radioList(['free' => 'Free', 'pro' => 'Pro']);<div class="form-field mb-3 field-signupform-topics"><label class="form-label">Topics</label><input type="hidden" name="SignupForm[topics]" value=""><div id="signupform-topics" class="d-flex flex-wrap gap-5"><div class="form-field"><input type="checkbox" id="i0" class="check" name="SignupForm[topics][]" value="php" checked><label for="i0">PHP</label></div><div class="form-field"><input type="checkbox" id="i1" class="check" name="SignupForm[topics][]" value="js"><label for="i1">JavaScript</label></div></div>
<div class="invalid-feedback"></div></div><div class="form-field mb-3 field-signupform-plan"><label class="form-label">Plan</label><input type="hidden" name="SignupForm[plan]" value=""><div id="signupform-plan" class="form-group" role="radiogroup"><div class="form-field"><input type="radio" id="i2" class="radio" name="SignupForm[plan]" value="free"><label for="i2">Free</label></div><div class="form-field"><input type="radio" id="i3" class="radio" name="SignupForm[plan]" value="pro" checked><label for="i3">Pro</label></div></div>
<div class="invalid-feedback"></div></div>Select, range, static control, and input group
Section titled “Select, range, static control, and input group”echo $form->field($model, 'country')->dropDownList(['es' => 'Spain', 've' => 'Venezuela'], ['prompt' => 'Select...']);echo $form->field($model, 'volume')->rangeInput( ['min' => 0, 'max' => 100, 'rangeOptions' => ['data-bs-bubble' => 'true']],);echo $form->field($model, 'username')->staticControl();echo $form->field( $model, 'amount', ['inputTemplate' => '<div class="input-group"><span class="input-group-text">$</span>{input}</div>'],);<div class="form-field mb-3 field-signupform-country"><label class="form-label" for="signupform-country">Country</label><select id="signupform-country" class="form-control" name="SignupForm[country]"><option value="">Select...</option><option value="es">Spain</option><option value="ve">Venezuela</option></select>
<div class="invalid-feedback"></div></div><div class="form-field mb-3 field-signupform-volume"><label class="form-label" for="signupform-volume">Volume</label><div class="form-range" data-bs-bubble="true"><input type="range" id="signupform-volume" class="form-range-input" name="SignupForm[volume]" value="50" min="0" max="100"></div>
<div class="invalid-feedback"></div></div><div class="form-field mb-3 field-signupform-username"><label class="form-label" for="signupform-username">Username</label><input type="text" id="signupform-username" class="form-control-plaintext" value="admin" readonly>
<div class="invalid-feedback"></div></div><div class="form-field mb-3 field-signupform-amount"><label class="form-label" for="signupform-amount">Amount</label><div class="input-group"><span class="input-group-text">$</span><input type="text" id="signupform-amount" class="form-control" name="SignupForm[amount]"></div>
<div class="invalid-feedback"></div></div>Bootstrap 6 uses .form-control for <select> elements too; rangeInput() registers the plugin bundle that
initializes the range.
Client-side validation styling and error summary
Section titled “Client-side validation styling and error summary”$form = ActiveForm::begin(['action' => '/signup', 'nativeValidation' => true, 'options' => ['novalidate' => true]]);echo $form->errorSummary($model);echo $form->field($model, 'email');echo Html::submitButton('Sign up', ['class' => 'btn-solid theme-primary']);ActiveForm::end();<form id="w0" action="/signup" method="post" novalidate data-bs-validate><div class="alert theme-danger"><p>Please fix the following errors:</p><ul><li>Email is not a valid email address.</li></ul></div><div class="form-field mb-3 field-signupform-email required"><label class="form-label" for="signupform-email">Email</label><input type="text" id="signupform-email" class="form-control is-invalid" name="SignupForm[email]" value="not-an-email" aria-required="true" aria-invalid="true">
<div class="invalid-feedback">Email is not a valid email address.</div></div><button type="submit" class="btn-solid theme-primary">Sign up</button></form>Widgets as fields
Section titled “Widgets as fields”Input widgets work with ActiveField::widget() (the tags, fruit, and birthday attributes of SignupForm):
use yii2\extensions\bootstrap6\{Chips, Combobox, Datepicker};
echo $form->field($model, 'tags')->widget(Chips::class, ['placeholder' => 'Add a tag']);echo $form->field($model, 'fruit')->widget(Combobox::class, ['items' => ['apple' => 'Apple', 'pear' => 'Pear']]);echo $form->field($model, 'birthday')->widget(Datepicker::class, ['inline' => true]);HTML helpers
Section titled “HTML helpers”use yii2\extensions\bootstrap6\Html;use yii2\extensions\bootstrap6\enum\Theme;
echo Html::badge('New', Theme::PRIMARY);echo Html::closeButton(['data-bs-dismiss' => 'alert']);echo Html::staticControl('email@example.com', ['id' => 'staticEmail']);echo Html::checkbox('notify', true, ['switch' => true, 'label' => 'Email me']);echo Html::radioList( 'plan', 'pro', ['free' => 'Free', 'pro' => 'Pro'], ['itemOptions' => ['wrapperOptions' => ['class' => 'form-field-card']]],);
$options = ['class' => 'card'];Html::theme($options, Theme::ACCENT);echo Html::tag('div', 'Card', $options);<span class="badge theme-primary">New</span><button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button><input type="text" id="staticEmail" class="form-control-plaintext" value="email@example.com" readonly><div class="switch"><input type="checkbox" id="i0" name="notify" value="1" checked role="switch" switch></div><label for="i0">Email me</label><div class="form-group"><div class="form-field form-field-card"><input type="radio" id="i1" class="radio" name="plan" value="free"><label for="i1">Free</label></div><div class="form-field form-field-card"><input type="radio" id="i2" class="radio" name="plan" value="pro" checked><label for="i2">Pro</label></div></div><div class="card theme-accent">Card</div>JavaScript
Section titled “JavaScript”Plugin options and client events
Section titled “Plugin options and client events”use yii2\extensions\bootstrap6\Toast;
echo Toast::widget( [ 'body' => 'Saved.', 'clientOptions' => ['autohide' => false], 'clientEvents' => ['hidden.bs.toast' => 'function (event) { console.log(event.type); }'], ],);The widget registers, at the end of the body:
import("/assets/<hash>/bootstrap.bundle.js").then((bootstrap) => { bootstrap.Toast.getOrCreateInstance(document.getElementById("w0"), {"autohide":false}); });document.getElementById("w0").addEventListener("hidden.bs.toast", function (event) { console.log(event.type); });Accessing plugin instances with import()
Section titled “Accessing plugin instances with import()”The bundle is an ES module without globals. Import the same URL the page loaded, so the browser reuses the evaluated
module; BootstrapPluginAsset::importJs() builds the call from PHP:
use yii2\extensions\bootstrap6\Dialog;use yii2\extensions\bootstrap6\asset\BootstrapPluginAsset;
Dialog::begin(['title' => 'Saved', 'options' => ['id' => 'confirm']]);echo '<p>Opened from PHP through <code>import()</code>.</p>';Dialog::end();
$bundle = BootstrapPluginAsset::register($this);
$this->registerJs( $bundle->importJs($this, 'bootstrap.Dialog.getOrCreateInstance(document.getElementById("confirm")).show();'),);In your own module scripts, read the URL with $bundle->moduleUrl($this) and import it:
const { Dialog } = await import(moduleUrl);
Dialog.getOrCreateInstance(document.getElementById("confirm")).show();Accessing plugin instances through window.bootstrap
Section titled “Accessing plugin instances through window.bootstrap”Enable exposeGlobal (see the installation guide) for
scripts written against the Bootstrap 5 global. The assignment happens when the module import resolves, so wait for
the load event:
window.addEventListener("load", () => { bootstrap.Toast.getOrCreateInstance(document.getElementById("w0")).show();});