components/jqueryui-theme-bootstrap
A jQuery UI theme that matches Bootstrap's look and feel, so widgets like datepickers, dialogs, sliders, and accordions blend seamlessly into Bootstrap-based interfaces. Part of the components/jqueryui family, it applies Bootstrap styling conventions to jQuery UI components without extra custom CSS.
This is an asset-only Composer package: it ships the Bootstrap-flavoured skin for jQuery UI (CSS/LESS plus the images/ui-icons_*.png sprite set). There is no service provider, no config file, and no PHP. Treat it as a static dependency you copy into your public directory.
1. Install the theme, plus the libraries it does not include. The theme only styles jQuery UI — it does not ship jQuery or jQuery UI itself. The components/* family of packages is the intended companion set:
composer require components/jqueryui-theme-bootstrap
# you almost certainly also need:
composer require components/jquery components/jqueryui
2. Find the files. Everything lands under:
vendor/components/jqueryui-theme-bootstrap/
├── jquery-ui.css # the compiled theme (structure + Bootstrap skin)
├── jquery-ui.min.css
├── jquery-ui.theme.css # skin only, pairs with a structural base
├── images/ # ui-icons_*.png, ui-bg_*.png — referenced relatively
└── less/ (or sass/) # variables you can recompile, if present in your tag
3. Copy into public/ via your build tool. Do not @import from vendor/ at runtime — it is outside the document root.
Laravel Mix:
mix.copy('vendor/components/jqueryui-theme-bootstrap/jquery-ui.min.css', 'public/css/jquery-ui.css')
.copyDirectory('vendor/components/jqueryui-theme-bootstrap/images', 'public/css/images')
.copy('vendor/components/jquery/jquery.min.js', 'public/js/jquery.js')
.copy('vendor/components/jqueryui/jquery-ui.min.js', 'public/js/jquery-ui.js');
Vite (Laravel 10+), pulling the CSS in as a module instead:
// resources/css/app.css — but the images must be copied, see Gotchas
@import '../../vendor/components/jqueryui-theme-bootstrap/jquery-ui.css';
4. Wire it into a layout, in the right order. Bootstrap first, theme second, your overrides last. jQuery before jQuery UI.
{{-- resources/views/layouts/app.blade.php --}}
<link rel="stylesheet" href="{{ asset('css/bootstrap.min.css') }}">
<link rel="stylesheet" href="{{ asset('css/jquery-ui.css') }}">
<link rel="stylesheet" href="{{ asset('css/app.css') }}"> {{-- your overrides LAST --}}
<script src="{{ asset('js/jquery.js') }}"></script>
<script src="{{ asset('js/jquery-ui.js') }}"></script>
@stack('scripts')
5. First use case — a datepicker that doesn't look like 2010. This is 90% of why anyone installs this package:
<div class="form-group">
<label for="starts_at">Starts at</label>
<input type="text" name="starts_at" id="starts_at"
class="form-control @error('starts_at') is-invalid @enderror"
value="{{ old('starts_at', optional($event->starts_at)->format('Y-m-d')) }}" autocomplete="off">
</div>
@push('scripts')
<script>
$(function () {
$('#starts_at').datepicker({
dateFormat: 'yy-mm-dd', // NOT 'Y-m-d' — see Gotchas
changeMonth: true,
changeYear: true,
});
});
@endpush
Wrap widget bootstrapping in a page-level partial. Rather than sprinkling $(function(){...}) through Blade, keep one resources/js/widgets.js that initialises by data attribute, and let Blade opt in declaratively:
<input type="text" class="form-control" data-widget="datepicker"
data-datepicker-options='{"dateFormat":"yy-mm-dd","minDate":0}'>
// resources/js/widgets.js
$(function () {
$('[data-widget="datepicker"]').each(function () {
$(this).datepicker($(this).data('datepicker-options') || {});
});
});
Datepicker + Laravel validation is the most common pairing. Align formats explicitly and validate server-side:
$request->validate([
'starts_at' => ['required', 'date_format:Y-m-d', 'after_or_equal:today'],
]);
Autocomplete against a Laravel route — keep the controller returning the exact shape jQuery UI expects ([{label, value}]), not an Eloquent collection:
Route::get('/users/search', function (Request $request) {
return User::query()
->where('name', 'like', $request->q.'%')
->limit(10)
->get(['name as label', 'id as value']);
});
$('#user').autocomplete({
source: (request, response) => $.getJSON('/users/search', { q: request.term }, response),
minLength: 2,
select: (e, ui) => { $('#user_id').val(ui.item.value); },
});
Confirmation dialogs. If you prefer jQuery UI dialogs over Bootstrap modals (common when you want a synchronous confirm() replacement), use the themed dialog and return a Promise so it composes with your form handlers:
window.confirmDialog = (message) => new Promise((resolve) => {
$('<div>').text(message).appendTo('body').dialog({
modal: true, title: 'Please confirm', width: 400,
buttons: {
Cancel: function () { $(this).dialog('close'); resolve(false); },
OK: function () { $(this).dialog('close'); resolve(true); },
},
close: function () { $(this).remove(); },
});
});
$('form[data-confirm]').on('submit', async function (e) {
if (!this.dataset.confirmed) {
e.preventDefault();
if (await confirmDialog(this.dataset.confirm)) {
this.dataset.confirmed = 1;
this.submit();
}
}
});
Rebrand the theme instead of fighting it. If your tag ships the LESS sources, the cheapest long-term win is recompiling the theme with your Bootstrap variables rather than adding an override stylesheet:
// resources/less/jquery-ui-custom.less
@brand-primary: #4f46e5; // match your app
@import '../../vendor/components/jqueryui-theme-bootstrap/less/jquery-ui.less';
Compile it through Mix (mix.less(...)) and ship only the result. This avoids the specificity war entirely.
Keep a tiny override file for the remainder. Whatever is left — sizing inside .input-group, .ui-datepicker z-index against Bootstrap dropdowns — belongs in one clearly-labelled file loaded after the theme, never as edits to vendor/.
Pin it. With zero dependents and no maintenance signal, commit the compiled CSS + images/ into public/ (or a resources/vendor/ folder) so a composer update cannot silently change your UI. If you keep it as a Composer dep, pin an exact version and commit composer.lock.
The image sprite will 404 if you copy only the CSS. jquery-ui.css references images/ui-icons_222222_256x240.png relative to the stylesheet's own URL. If public/css/jquery-ui.css is served from /css/, the browser requests /css/images/ui-icons_*.png. Copy the images/ directory next to the CSS file, or rewrite the url(...) paths in a build step. A missing sprite shows up as blank datepicker arrows and a broken close "X" on dialogs — check the Network tab first.
No vendor:publish, no service provider. There is nothing artisan-aware here. Everything is a manual copy step in Mix/Vite. New team members will ask why the datepicker is unstyled — the answer is always "did you run npm run build / the copy task?"
Bootstrap 3 only. This theme is a Bootstrap 3-era skin (.panel, .input-group-addon, @brand-primary, pull-right). On Bootstrap 4/5 the controls will render with wrong spacing, border-radius, and font sizes, and some .ui-* rules will lose specificity to BS5's reboot. If you are on BS5, budget for real override work or pick a maintained alternative.
Class-name collisions with Bootstrap. Both libraries define .tooltip, and jQuery UI's dialog overlay fights Bootstrap's .modal-backdrop (double dimming, stuck scroll lock). Pick one dialog/tooltip library per feature and be consistent. When both are on the page, scope your overrides: .ui-dialog, .ui-widget-overlay { z-index: ... } — Bootstrap's modal z-index (1050+) is higher than jQuery UI's defaults (~100).
dateFormat: 'yy-mm-dd' vs PHP's Y-m-d. jQuery UI uses yy for a four-digit year and y for two. PHP's Y is not a valid jQuery UI token. This mismatch produces "2024" being parsed as year 20 in some paths, or a silent validation failure. Same class of bug for mm (month, zero-padded) vs MM (month name) and dd vs DD (day-of-year).
Load order matters and is unforgiving. Bootstrap CSS → theme CSS → your CSS. jQuery → jQuery UI → your JS. Any other order gives you either unstyled widgets or $(...).datepicker is not a function.
jQuery UI JS is often stale in these packaged components. Older tags carry jQuery UI 1.10/1.11, which predates the 1.13 XSS fixes in .datepicker and the _focusable handling. If you are only using datepicker/dialog/autocomplete, strongly consider components/jqueryui at a pinned 1.13.x, or jquery-ui-dist from npm, and keep this package purely for the CSS.
Extension point reality check. The only supported customisation surface is (a) recompiling the LESS/SASS with your variables, or (b) a later-loaded override stylesheet. There are no CSS custom properties, no config file, no theme hook. Do not patch vendor/ — a composer install on a fresh CI box will erase it.
Debugging checklist, in order: (1) Network tab for 404 image sprites; (2) confirm the theme CSS actually loaded after Bootstrap in the DOM; (3) getComputedStyle on the offending .ui-* element — if a Bootstrap selector wins, you have a specificity/order problem, not a theme problem; (4) confirm $.ui.version in the console matches what the theme was built against; (5) disable your own app.css to prove the conflict is yours.
Risk assessment for day-to-day use. Score 0, no stars, no dependents, and a bootstrap-3-era skin means: no upstream fixes, no security response, and you are the maintainer. That is acceptable for a pinned, vendored, cosmetic CSS file in a legacy app. For a new Laravel project, prefer jquery-ui + jquery-ui-dist from npm with a Bootstrap 5-compatible theme, or drop jQuery UI in favour of Flatpickr/Tom Select/Alpine for the handful of widgets you actually need — you will spend less time on this package's quirks than on the one migration away from it.
How can I help you explore Laravel packages today?