Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.

lbecket's avatar
Level 39

Dynamic Progress Bar Color in Inertia

I've got a new Laravel 11 project with Breeze and Vue/Inertia with TypeScript. I'm trying to find a way to set Inertia's progress bar color dynamically based on a prop on the User model, but it doesn't seem that the user exists at the time createInertiaApp is instantiated. Is there any kind of lifecycle hook that I can use to set this color within app.ts and, if not, where else can I set this so that I can be assured of having an authenticated user available?

If it helps, here's my app.ts:

import "./bootstrap";
import "../css/app.css";
import "@mdi/font/css/materialdesignicons.css";

import { createApp, h, DefineComponent } from "vue";
import { createInertiaApp } from "@inertiajs/vue3";
import { resolvePageComponent } from "laravel-vite-plugin/inertia-helpers";
import { ZiggyVue } from "../../vendor/tightenco/ziggy";
import { createPinia } from "pinia";

import "vuetify/styles";
import { createVuetify } from "vuetify";
import * as components from "vuetify/components";
import * as directives from "vuetify/directives";
import useTheme from "./Hooks/useTheme";

const vuetify = createVuetify({
  components,
  directives,
  icons: {
    defaultSet: "mdi",
  },
  theme: useTheme(),
});

const pinia = createPinia();
const appName = import.meta.env.VITE_APP_NAME || "Laravel";

createInertiaApp({
  title: (title) => `${title} - ${appName}`,
  resolve: (name) => resolvePageComponent(`./Pages/${name}.vue`, import.meta.glob<DefineComponent>("./Pages/**/*.vue")),
  setup({ el, App, props, plugin }) {
    createApp({ render: () => h(App, props) })
      .use(plugin)
      .use(ZiggyVue)
      .use(vuetify)
      .use(pinia)
      .mount(el);
  },
  progress: {
    color: "#FF0000",  <-- MAKE DYNAMIC BASED ON USER VALUE
  },
});
0 likes
4 replies
gych's avatar
gych
Best Answer
Level 29

You can disable the default progress bar and create your own by using the inertia router 'start' and 'finish' event listeners.

First disable the default progress in app.js

progress: false,

Then use the router event listeners in your main layout component, in the example below I just added two console logs but on the start method you should trigger/display the loading element and then on finish hide it again. This is how you can completely customize it.

import { router } from '@inertiajs/vue3'

router.on('start', () => {
    console.log('router start');
})

router.on('finish', () => {
    console.log('router finish');
})
1 like
lbecket's avatar
Level 39

@gych thanks, this got me pointed in the right direction and I was ultimately able to get it working by following the Inertia docs and installing NProgress. I'm using Vuetify themes and need the ability to set the progress bar's color based on the user's selected theme.

For anyone else trying this, here's a layout file that dynamically sets the progress bar color to match the Vuetify theme, based off of a prop that's stored on the User model:

<script setup lang="ts">
  import { AuthenticatedSideNav } from "@/Components";
  import { usePage, router } from "@inertiajs/vue3";
  import useTheme from "@/Hooks/useTheme";
  import NProgress from "nprogress";
  import { watchEffect } from "vue";

  const user = usePage().props.auth.user;
  const theme = useTheme();
  const primaryColor = theme.themes[user.theme as keyof typeof theme.themes].colors?.primary;

  watchEffect(() => {
    document.documentElement.style.setProperty("--primary-color", primaryColor || "");
  });

  let timeout: number | null | undefined = null;

  router.on("start", () => {
    timeout = setTimeout(() => NProgress.start(), 250);
  });

  router.on("progress", (event) => {
    if (NProgress.isStarted() && event.detail.progress?.percentage) {
      NProgress.set((event.detail.progress.percentage / 100) * 0.9);
    }
  });

  router.on("finish", (event) => {
    if (timeout !== null) {
      clearTimeout(timeout);
    }
    if (!NProgress.isStarted()) {
      return;
    } else if (event.detail.visit.completed) {
      NProgress.done();
    } else if (event.detail.visit.interrupted) {
      NProgress.set(0);
    } else if (event.detail.visit.cancelled) {
      NProgress.done();
      NProgress.remove();
    }
  });
</script>

<template>
  <v-theme-provider
    :theme="user.theme"
    with-background
  >
    <v-app>
      <AuthenticatedSideNav :activeTheme="user.theme" />
      <v-main>
        <slot />
      </v-main>
    </v-app>
  </v-theme-provider>
</template>

<style>
  #nprogress .bar {
    background: var(--primary-color) !important;
    height: 3px !important;
  }

  #nprogress .peg {
    box-shadow: none !important;
  }
</style>

There may be a cleaner way to do this, but this is getting it done for me.

2 likes
TohidMth's avatar

Hi, Do this.

app.js

createInertiaApp({
    ...
    progress: {
        color: "var(--primary)",
    },
});

scss:

:root {
    --primary: #0060ff;
}

MainLayout.vue or ...

document.documentElement.style.setProperty("--primary", usePage().props.template.get_options.main_color);
lbecket's avatar
Level 39

@TohidMth I'm not sure that I'm following. template doesn't even exist on usePage().props. This also doesn't reference the value that I need from the User model. Regardless, my solution above is getting it done.

Please or to participate in this conversation.