This guide will help you integrate your Laravel Jetstream application with vikdiesel/admin-one-vue-bulma-dashboard Vue Bulma Buefy dashboard.
Please note: this document is work in progress, so some things are missing.
Simple, beautiful and free Vue.js 2.x Bulma Buefy admin dashboard for Laravel 9.x Jetstream Inertia + Vue stack
- Built with Vue.js 2.x Bulma and Buefy
- Laravel 9.x Jetstream Inertia + Vue stack
- Laravel Mix
- Classic Options API using
data
,computed
,methods
, etc. - SPA with Vuex & Inertia Router
- Styled scrollbars
- SCSS sources with variables
- Reusable components
- Responsive
- Free under MIT License
- Premium versions available
- Install
- Copy styles, components and scripts
- Add pages
- Fix router links
- Add Inertia-related stuff
- Work in progress
- Laravel 7.x & 8.x
- Install Laravel application
- Install Jetstream with Inertia + Vue stack
cd
to project dir- Move
resources/js
folder toresources-temp/js
. These js files will serve as a reference during development process (just in case, you'll ever need to extract some logic, that is missing here) - run
npm remove @inertiajs/inertia-vue3 @vue/compiler-sfc @tailwindcss/forms @tailwindcss/typography postcss postcss-import tailwindcss
- run
npm i vuex@^3 vue@^2 vue-loader@^15 @vue/composition-api @inertiajs/inertia-vue bulma buefy chart.js vue-chartjs numeral sass sass-loader -D
Replace postCss()
with sass()
and app.scss
with main.scss
in webpack.mix.js
:
mix.js('resources/js/app.js', 'public/js').vue()
.sass('resources/scss/main.scss', 'public/css')
.alias({
'@': 'resources/js',
})
Clone either vikdiesel/admin-one-vue-bulma-dashboard or vikdiesel/admin-two-vue-bulma-dashboard project locally into a separate folder
Next, copy these files from cloned dashboard project directory to laravel project directory:
- Copy
src/components
src/store
src/menu.js
toresources/js/
- Copy
src/App.vue
andsrc/FullPage.vue
toresources/Layouts/
- Copy
src/css
andsrc/scss
toresources/
- Copy
src/assets/justboil-logo.svg
toresources/js/images/
- Delete
resources/css/app.css
- Copy
resources
directory from this repository to to laravel project
Replace app.css
with main.css
:
<!-- Styles -->
<link rel="stylesheet" href="{{ mix('css/main.css') }}">
Add before </body>
:
<link href="https://cdn.materialdesignicons.com/4.9.95/css/materialdesignicons.min.css" rel="stylesheet" type="text/css">
Replace <router-view />
with <slot />
Add this.$store.dispatch('toggleFullPage', false)
to created()
lifecycle hook
Add this.$store.dispatch('toggleFullPage', true)
to created()
lifecycle hook
Remove beforeDestroy()
lifecycle hook
Add import filter from 'lodash/filter'
Add filter()
and replace this.$slots.default
with slots
in render()
method, so you'll get:
render (createElement) {
const renderAncestor = elements => createElement(
// ...
)
const slots = filter(this.$slots.default, slot => !!slot.tag)
if (slots.length <= this.maxPerRow) {
return renderAncestor(slots)
} else {
return createElement(
'div',
{ attrs: { class: 'is-tiles-wrapper' } },
chunk(slots, this.maxPerRow).map(group => {
return renderAncestor(group)
})
)
}
}
Let's just add first page. You can repeat these steps for other pages, if you wish to. If you've followed previous steps, there's already resources/js/Pages/HomeExample.vue
for your reference.
First, copy src/views/Home.vue
(original dashboard project) to resources/js/Pages/
(your Laravel project).
Add Head
. Then, wrap page contents into App
Layout component:
<template>
<app>
<Head title="Dashboard" />
<title-bar :title-stack="titleStack" />
<!-- ... -->
</app>
</template>
<script>
import { Head } from '@inertiajs/inertia-vue'
import App from '@/Layouts/App.vue'
// ...
export default defineComponent({
name: 'Home',
components: {
Head,
App,
// ...
}
// ...
})
</script>
Add route in routes/web.php
. There's a /dashboard
route already defined by default, so just replace Inertia::render('Dashboard')
with Inertia::render('Home')
:
Route::get('/dashboard', function () {
return Inertia::render('Home');
})->name('dashboard');
Here we replace router-link with Inertia Link.
Optionally, you can pass menu via Inertia shared props, so it's going to be controlled with PHP. Here we'd just use JS.
to
should be replaced with route
which specifies route name defined in routes/web.php
. For external links href
should be used instead. Here's an example for menu.js
:
export default [
'General',
[
{
route: 'dashboard',
icon: mdiDesktopMac,
label: 'Dashboard'
},
{
href: 'https://example.com/',
icon: mdiDesktopMac,
label: 'Example.com'
}
]
]
Route names reflect ones defined in routes/web.php
:
Route::get('/dashboard', function () {
return Inertia::render('Home');
})->name('dashboard');
Now, let's update vue files, to make them work with route names and Inertia links.
Add Link
import to <script>
:
<script>
import { Link } from '@inertiajs/inertia-vue'
// ...
</script>
Replace componentIs
in computed{}
with:
<script>
export default defineComponent({
// ...
computed: {
componentIs () {
return this.item.route ? Link : 'a'
}
// ...
}
// ...
})
</script>
Replace <component>
attrs with:
<template>
<component
:is="componentIs"
:href="item.route ? $route(item.route) : item.href"
:target="item.target"
:class="{ 'has-icon': !!item.icon, 'has-dropdown-icon': hasDropdown, 'is-active': item.route && $route().current(item.route) }"
@click="menuClick"
>
<!-- ... -->
</component>
</template>
Import and register Link
component:
<script>
import { Link } from '@inertiajs/inertia-vue'
// ...
export default defineComponent({
components: {
Link
// ...
}
// ...
})
</script>
Replace <router-link>
with <Link>
:
<template>
<Link
:href="$route('dashboard')"
class="navbar-item"
:class="{ 'is-active': $route().current('dashboard') }"
>
<!-- ... -->
</Link>
</template>
Replace this.$router
with Inertia
:
<script>
import { Inertia } from '@inertiajs/inertia'
// ...
export default defineComponent({
// ...
mounted () {
Inertia.on('navigate', (event) => {
this.isMenuActive = false
})
}
// ...
})
</script>
Fix newAvatar
computed property, so it fetches profile photo from backend:
<script>
export default defineComponent({
// ...
computed: {
newAvatar () {
return this.avatar ? this.avatar : this.$page.props.user.profile_photo_url
}
}
})
</script>
Update userName
and logout
:
<script>
export default defineComponent({
// ...
computed: {
// ...
userName () {
return this.$page.props.user.name
},
...mapState([
'isAsideMobileExpanded',
'isNavBarVisible',
// remove 'userName'
])
},
methods: {
// ...
logout () {
Inertia.post(route('logout'))
}
}
// ...
})
</script>
As mentioned, this guide is WIP - work in progress. Contributions open. Here's the list of what's missing right now:
- Pages for resources/Pages/API
- Pages for resources/Pages/Auth (except Login.vue and Register.vue)
- Pages for resources/Pages/Profile
- Unused default Jetstream files list
If you're using an older version of Laravel, please follow Laravel 7.x & 8.x guide.