Published:

Vue 3 + Pinia - Redirect to Previous URL After Login

In this post we'll cover how to redirect a user back to their originally requested url / route after logging into a Vue 3 + Pinia application. This is done with the help of a Vue Router Navigation Guard and a Pinia Auth Store.

The code below is part of a larger JWT login example I posted recently, I created this separate post to describe the redirection behaviour because I couldn't find many clear answers out there that show how to implement it in Vue 3. For the full tutorial and a working demo of the code go to Vue 3 + Pinia - JWT Authentication Tutorial & Example.

 

Vue 3 Router with Navigation Guard

Path: /src/helpers/router.js

The router defines the routes for the Vue 3 application and creates a new Vue Router instance with the createRouter() function. The exported router instance is imported into main.js where it is passed to the Vue app on startup.

The createRouter() function is part of Vue Router v4 which is compatible with Vue 3, the previous version of the router (Vue Router v3) is compatible with Vue 2. For more info on what's changed in the new version of the Vue Router see https://next.router.vuejs.org/guide/migration/.

The home route maps the root path ('/') of the app to the HomeView component and the '/login' route maps to the LoginView component.

Vue Navigation Guard

Navigation Guards in Vue 3 are used to protect routes, they can be implemented globally, on a single route, or inside a Vue component. This example uses the router.beforeEach() hook to register a global navigation guard.

The guard function checks if the user is logged in by checking if there is a current user object in the Pinia auth store. If not logged in the function saves the requested path (to.fullPath) in the Pinia auth store (auth.returnUrl) and redirects to the login page by returning the path '/login'. If logged in the guard function does nothing and the route is allowed to proceed.

For more information on Vue Router Navigation Guards see https://router.vuejs.org/guide/advanced/navigation-guards.html.

import { createRouter, createWebHistory } from 'vue-router';

import { useAuthStore } from '@/stores';
import { HomeView, LoginView } from '@/views';

export const router = createRouter({
    history: createWebHistory(import.meta.env.BASE_URL),
    linkActiveClass: 'active',
    routes: [
        { path: '/', component: HomeView },
        { path: '/login', component: LoginView }
    ]
});

router.beforeEach(async (to) => {
    // redirect to login page if not logged in and trying to access a restricted page
    const publicPages = ['/login'];
    const authRequired = !publicPages.includes(to.path);
    const auth = useAuthStore();

    if (authRequired && !auth.user) {
        auth.returnUrl = to.fullPath;
        return '/login';
    }
});
 

Pinia Auth Store

Path: /src/stores/auth.store.js

The auth store contains Pinia state and actions for authentication. The user state property holds the current logged in user, it is initialized with the 'user' object from local storage to support staying logged in between page refreshes and browser sessions, or null if localStorage is empty. The returnUrl is used to redirect the user to the previous url after successful login, it is set by the router before it redirects an unauthenticated user to the login page.

The Pinia login() action method posts credentials to the API, on success the returned user object is stored in Pinia state and localStorage, and the router redirects to the return url or home page. On fail the async method throws an error which is caught and displayed inside the LoginView component.

The logout() action method sets the user to null in Pinia state, removes it from localStorage and redirects to the login page.

import { defineStore } from 'pinia';

import { fetchWrapper, router } from '@/helpers';

const baseUrl = `${import.meta.env.VITE_API_URL}/users`;

export const useAuthStore = defineStore({
    id: 'auth',
    state: () => ({
        // initialize state from local storage to enable user to stay logged in
        user: JSON.parse(localStorage.getItem('user')),
        returnUrl: null
    }),
    actions: {
        async login(username, password) {
            const user = await fetchWrapper.post(`${baseUrl}/authenticate`, { username, password });

            // update pinia state
            this.user = user;

            // store user details and jwt in local storage to keep user logged in between page refreshes
            localStorage.setItem('user', JSON.stringify(user));

            // redirect to previous url or default to home page
            router.push(this.returnUrl || '/');
        },
        logout() {
            this.user = null;
            localStorage.removeItem('user');
            router.push('/login');
        }
    }
});
 


Subscribe or Follow Me For Updates

Subscribe to my YouTube channel or follow me on Twitter, Facebook or GitHub to be notified when I post new content.

Other than coding...

I'm currently attempting to travel around Australia by motorcycle with my wife Tina on a pair of Royal Enfield Himalayans. You can follow our adventures on YouTube, Instagram and Facebook.


Need Some Vue 3 Help?

Search fiverr to find help quickly from experienced Vue 3 developers.



Supported by