Building a Page Layout for Vue Router

Part 12 of 48 in API Driven Development With Laravel and VueJS
Dan Pastori avatar
Dan Pastori October 23rd, 2017
⚡️ Updated content is available We learned a lot since we originally wrote this article. We now have this updated for Laravel 10, Vue 3/NuxtJS 3, and Capacitor 3.

One thing that comes in real handy with any app is the ability to add layouts to your application. Laravel does that with the Blade templating system: Blade Templates – Laravel – The PHP Framework For Web Artisans. However, since we are doing a Single Page Application, doing that with Vue Router is a little bit of a trick. The way I like to do it is create a root level page that contains the Vue components present on every page, like Header and Footer. The other advantage of using a layout, is you can load all of the Vuex data you need once and it will be present on all of the child pages, which in this case, is our entire app.

Step 1: Restructure To Nested Routes

In the tutorial where we set up our /assets/js/routes.js file: https://serversideup.net/configuring-vue-router-single-page-app/ we didn’t include any nested routes. The nested feature of Vue Router is the trick that I use to make a layout. For more information on nested routes, check out: Nested Routes · vue-router

First we will build our top level route which will be our Layout. To do this, add the following route object to your routes file:

{
    path: '/',
    name: 'layout',
    component: Vue.component( 'Home', require( './pages/Layout.vue' ) ),
}

We will be adding the Layout.vue in the next step. Now we will add a children key that will contain the array of routes that are nested routes. We should have a routes.js file that looks like:

/*
|-------------------------------------------------------------------------------
| routes.js
|-------------------------------------------------------------------------------
| Contains all of the routes for the application
*/

/*
    Imports Vue and VueRouter to extend with the routes.
*/
import Vue from 'vue'
import VueRouter from 'vue-router'

/*
    Extends Vue to use Vue Router
*/
Vue.use( VueRouter )

/*
    Makes a new VueRouter that we will use to run all of the routes
    for the app.
*/
export default new VueRouter({
    routes: [
        {
            path: '/',
            name: 'layout',
            component: Vue.component( 'Layout', require( './pages/Layout.vue' ) ),
            children: []
        }
});

Now we will take the existing routes and add them to the children array to get a file that looks like:

/*
|-------------------------------------------------------------------------------
| routes.js
|-------------------------------------------------------------------------------
| Contains all of the routes for the application
*/

/*
    Imports Vue and VueRouter to extend with the routes.
*/
import Vue from 'vue'
import VueRouter from 'vue-router'

/*
    Extends Vue to use Vue Router
*/
Vue.use( VueRouter )

/*
    Makes a new VueRouter that we will use to run all of the routes
    for the app.
*/
export default new VueRouter({
    routes: [
        {
            path: '/',
            name: 'layout',
            component: Vue.component( 'Layout', require( './pages/Layout.vue' ) ),
            children: [
                {
                    path: 'home',
                    name: 'home',
                    component: Vue.component( 'Home', require( './pages/Home.vue' ) )
                },
                {
                    path: 'cafes',
                    name: 'cafes',
                    component: Vue.component( 'Cafes', require( './pages/Cafes.vue' ) ),
                },
                {
                    path: 'cafes/new',
                    name: 'newcafe',
                    component: Vue.component( 'NewCafe', require( './pages/NewCafe.vue' ) )
                },
                {
                    path: 'cafes/:id',
                    name: 'cafe',
                    component: Vue.component( 'Cafe', require( './pages/Cafe.vue' ) )
                }
            ]
        }
    ]
});

Now we will add our Layout.vue page.

Step 2: Add Layout.vue

In the /resources/assets/js/pages directory add a file called Layout.vue. This will be our Layout file where we can add our Navigation component discussed in our last tutorial. Stub out the component like this:

<style>

</style>

<template>
  <div id="app-layout">
        <router-view></router-view>
  </div>
</template>

<script>
  export default {

  }
</script>

If you check out <router-view></router-view> that component is built into Vue Router which renders the child component inside of the parent component. It’s like a place holder for the nested component.

We now have our layout, so let’s import our Navigation.vue component so you can see the power of the layout. We just need to add the following line before we export the default component:

import Navigation from '../components/global/Navigation.vue';

That will include our Navigation component, we then need to tell our page to use it by adding it to the components object on the page:

<script>
  import Navigation from '../components/global/Navigation.vue';

  export default {
    components: {
      Navigation
    }
  }
</script>

Finally, we add the component to our template before the <router-view></router-view> component so our layout looks like this:

<style>

</style>

<template>
  <div id="app-layout">
    <navigation></navigation>

    <router-view></router-view>
  </div>
</template>

<script>
  import Navigation from '../components/global/Navigation.vue';

  export default {
    components: {
      Navigation,
    }
  }
</script>

What this means is now the Navigation component will appear on all pages since every page is a sub page of the layout. Kinda slick eh?

Let’s load the data we need on every page.

To do that, since we have Vuex ready to rock and roll: https://serversideup.net/build-vuex-module/, we simply bind to the created() hook and load some data!

This case, we know we want a user in the navigation component so we can display a profile picture, as well as loading the cafes. When we load it on the parent component, and since we are using Vuex, the user will be available to ALL routes. I didn’t write a tutorial about writing everything we need to load the user, but if you followed along with this series, it’s follows along the same steps as adding a cafe module, routes, models, etc. Of course all of the code can be found here: GitHub – serversideup/roastandbrew .

Our Layout.vue component should look like:

<style>

</style>

<template>
  <div id="app-layout">
    <navigation></navigation>

    <router-view></router-view>
  </div>
</template>

<script>
  import Navigation from '../components/global/Navigation.vue';

  export default {
    components: {
      Navigation,
    },

    created(){
      this.$store.dispatch( 'loadCafes' );
      this.$store.dispatch( 'loadUser' );
    }
  }
</script>

When created, the layout loads the cafes and user that’s currently logged into the app.

Step 3: Adjust the Authentication Controller Redirect

This is the last step of adding a Layout to the app. Since now the top level link is our layout, we have to direct users to our /home route upon login. To do this, open up the /app/Http/Controllers/Web/AuthenticationController.php and find the getSocialCallback() method. At the end of the method adjust the redirect from

return redirect('/');

to

return redirect('/#/home');

Now your users will be redirected to the home page on login.

Conclusion

Now that you have your layout created, you can add any sort of elements you want to appear on each page. I’ve added an AppFooter.vue as another example as a component that should be on every page. You can find it in the repository: GitHub – serversideup/roastandbrew. There are other ways you can pull this off if needed. One would be if you don’t want to access Vuex data, then you could add header and footer into your app.blade.php file and you can load the Vue data on a <div> element between the header and footer. I just prefer to do it this way since we can load global data and access Vuex information.

Keep Reading
View the course View the Course API Driven Development With Laravel and VueJS
Up Next → SPA Tutorial Progress Update

Support future content

The Ultimate Guide to Building APIs and Single-Page Applications with Laravel + VueJS + Capacitor book cover.

Psst... any earnings that we make off of our book is being reinvested to bringing you more content. If you like what you read, consider getting our book or get sweet perks by becoming a sponsor.

Written By Dan

Dan Pastori avatar Dan Pastori

Builder, creator, and maker. Dan Pastori is a Laravel certified developer with over 10 years experience in full stack development. When you aren't finding Dan exploring new techniques in programming, catch him at the beach or hiking in the National Parks.

Like this? Subscribe

We're privacy advocates. We will never spam you and we only want to send you emails that you actually want to receive. One-click unsubscribes are instantly honored.