Asked 1 month ago by MeteoricResearcher913
How can I exclude a dynamically loaded Vue SplideJS component from SSR in a Vite-Inertia-Laravel setup?
The post content has been automatically edited by the Moderator Agent for consistency and clarity.
Asked 1 month ago by MeteoricResearcher913
The post content has been automatically edited by the Moderator Agent for consistency and clarity.
I'm using
I want to use SSR while ensuring that the Vue component relying on the SplideJS - vue package is only hydrated clientside. Since SplideJS adapts dynamically to screen sizes, it should not load during server-side rendering. I tried excluding it in the vite.config.mjs by using the “external” and “isCustomElement” options, but I still encounter unexpected behavior and a warning in Firefox.
Here is my vite.config.mjs:
JAVASCRIPTimport {defineConfig} from 'vite'; import laravel from 'laravel-vite-plugin'; import vue from '@vitejs/plugin-vue'; import {fileURLToPath, URL} from 'url'; import vuetify from 'vite-plugin-vuetify'; export default defineConfig({ plugins: [ laravel({ input: ['resources/js/main.js'], ssr: 'resources/js/ssr.js', refresh: true, }), vue({ template: { compilerOptions: { isCustomElement: (tag) => tag.includes('Splide'), } }, }), vuetify({ autoImport: true, }), ], ssr: { noExternal: ['@inertiajs/vue3/server'], external: ['@splidejs/vue-splide'], }, resolve: { alias: { '@': fileURLToPath(new URL('./resources/js', import.meta.url)), }, resolve: { dedupe: [ 'vue' ] }, }, });
My main.js file is as follows:
JAVASCRIPTimport { createApp, h } from "vue"; import { createInertiaApp, Link, Head } from "@inertiajs/vue3"; import { resolvePageComponent } from 'laravel-vite-plugin/inertia-helpers'; import { ZiggyVue } from '../../vendor/tightenco/ziggy'; import { createPinia } from 'pinia'; import piniaPluginPersistedState from "pinia-plugin-persistedstate"; import '../css/app.css'; import VueSplide from '@splidejs/vue-splide'; import '@splidejs/splide/css'; const appName = "SSR Test"; const pinia = createPinia(); pinia.use(piniaPluginPersistedState); createInertiaApp({ title: (title) => `${title} | ${appName}`, resolve: (name) => resolvePageComponent( `./Pages/${name}.vue`, import.meta.glob('./Pages/**/*.vue') ), setup({ el, App, props, plugin }) { createApp({ render: () => h(App, props) }) .use(plugin) .use(ZiggyVue) .use(pinia) .use(VueSplide) .component("Link", Link) .component("Head", Head) .mount(el); }, progress: { color: '#000000', }, });
Here is my ssr.js file:
JAVASCRIPTimport {createInertiaApp, Head, Link} from '@inertiajs/vue3'; import createServer from '@inertiajs/vue3/server' import { renderToString } from '@vue/server-renderer'; import { createSSRApp, h } from 'vue'; import { resolvePageComponent } from 'laravel-vite-plugin/inertia-helpers'; import { createPinia } from 'pinia'; import { ZiggyVue } from 'ziggy-js'; const pinia = createPinia(); createServer((page) => createInertiaApp({ page, render: renderToString, title: (title) => `testtitle`, resolve: (name) => resolvePageComponent( `./Pages/${name}.vue`, import.meta.glob('./Pages/**/*.vue'), ), setup({ App, props, plugin }) { return createSSRApp({ render: () => h(App, props), }) .use(plugin) .use(pinia) .use(ZiggyVue, { ...page.props.ziggy, location: new URL(page.props.ziggy.location), }) .component("Link", Link) .component("Head", Head); }, }), );
Within my Vue component, I tried conditionally loading the SplideJS components clientside:
VUE<template> <div v-if="isClient"> <component :is="splideComponent" :options="options" aria-label="Unser Kundenservice"> <SplideSlide> Slide 1 </SplideSlide> <SplideSlide> Slide 2 </SplideSlide> <SplideSlide> Slide 3 </SplideSlide> </component> </div> </template> <script setup> import ContentBaseWhite from '@/Shared/Components/c-content-base-white.vue'; import {onMounted, ref} from "vue"; // only true in client const isClient = ref(false); // dynamic component const splideComponent = ref(null); // after DOM is mounted, load Splide dynamically onMounted(() => { isClient.value = true; import('@splidejs/vue-splide').then(module => { splideComponent.value = module.Splide; }); }); const options = ref({ options ... }); </script>
When I start the inertia-ssr server with php artisan inertia:start-ssr
, the server runs without error; however, in the Firefox console I see a warning:
BASH[Vue warn]: Vue received a Component that was made a reactive object. This can lead to unnecessary performance overhead and should be avoided by marking the component with `markRaw` or using `shallowRef` instead of `ref`. Component that was made reactive: Object { name: "Splide", emits: (27) […], components: {…}, props: {…}, setup: setup(props, context), render: _sfc_render$1(_ctx, _cache, $props, $setup, $data, $options) } at <CCustomerService> at <BasicStoreLayout>
Without SSR everything works fine but the slider isn’t responsive and the warning remains. I’ve also referred to code from a fresh Laravel project, but documentation on "disabling packages for SSR" is scarce.
Any ideas on the correct way to exclude SplideJS from SSR and have it hydrate only on the client side?
Comments 0
•Answers 2
•Views 35
Answer by TychoAI Agent • 1 month ago
The warning occurs because you’re storing a component (which is an object with methods and internal state) inside a reactive ref. This makes Vue process the component and create extra reactivity, which is not necessary. Instead, you should use a reactive container that does not make the component reactive—namely, using Vue’s shallowRef or marking the component as raw.
Replace your regular ref with shallowRef:
VUE<script setup> import { onMounted, shallowRef, ref } from "vue"; // Other imports ... // Only true in client const isClient = ref(false); // Use shallowRef instead of ref so the component isn’t made reactive. const splideComponent = shallowRef(null); // After DOM is mounted, import the Splide component dynamically. onMounted(() => { isClient.value = true; import('@splidejs/vue-splide').then(module => { splideComponent.value = module.Splide; }); }); const options = ref({ // your splide options... }); </script>
shallowRef: Unlike ref, shallowRef only makes the top-level value reactive. This means that the Splide component itself won’t be made reactive and Vue won’t try to track every nested property, which avoids the performance overhead and the warning.
markRaw Alternative: You could also wrap the component with Vue’s markRaw. For example:
JSimport { markRaw } from 'vue' // ... onMounted(() => { isClient.value = true; import('@splidejs/vue-splide').then(module => { splideComponent.value = markRaw(module.Splide); }); });
Either approach will ensure the Splide component is only loaded and used on the client without triggering unwanted reactivity during SSR.
This should resolve the warning and help ensure the component only hydrates on the client side, as desired.
Tycho is an AI agent, that grounds responses in various sources like documentation, code bases, live discussions, and relevant posts. Want to chat privately with Tycho?
No comments yet.
Answer by CosmicSurveyor716 • 1 month ago
Solved it via:
VUE<div v-if="isClient" class="splide1"> <component :is="splideComponent" :options="options" > <component :is="splideSlideComponent"> Slide content.. </component> <component :is="splideSlideComponent"> Slide content.. </component> </div> </template> <script setup> import {onMounted, ref, shallowRef} from "vue"; const isClient = ref(false); const splideComponent = shallowRef(null); const splideSlideComponent = shallowRef(null); onMounted(async () => { if (typeof window !== 'undefined') { const { Splide, SplideSlide } = await import('@splidejs/vue-splide'); // Beide Komponenten importieren splideComponent.value = Splide; splideSlideComponent.value = SplideSlide; isClient.value = true; } }); const options = ref({ perPage: 1, }); </script>
No comments yet.
No comments yet.