inertia icon indicating copy to clipboard operation
inertia copied to clipboard

CSS imports from old layouts persist, even if new layout is set

Open Arturexe opened this issue 3 years ago • 3 comments

Versions:

  • @inertiajs/inertia version: ^0.11.0
  • @inertiajs/inertia-vue3 version: ^0.6.0

Describe the problem:

When I use <inertia-link> to navigate to a new vue component, which happens to be assigned to a new layout template, the CSS of the old components layout template persists in the properties of the new component, if the css is imported with @import statement in the layout. The old CSS, which should be removed, can be observed in the browser dev tools. This creates a big CSS mess, because old properties perists forever and mix with components, which they are not written for. The only thing that removes the old CSS props is a page refresh.

This applies only to the imported CSS, not to the CSS written directly to the layouts. The layout's HTML behaves as usual.

One example of this happening is when I switch between frontend and backend of my app, which use different css files but are unified in one project directory.

Steps to reproduce:

  1. create 2 files each per vue component, layout, css file.
  2. link both component files with <inertia-link>
  3. assign one layout to each component
  4. import the corresponding css file into each layout with the @import statement
  5. use the link in the vue components to navigate back and forth and observe browser dev tools styles for CSS props from both css files.

inertia-layout.ts:

import type { Plugin } from 'vite'

const PLUGIN_NAME = 'vite:inertia:layout'
const TEMPLATE_LAYOUT_REGEX = /<template +layout(?: *= *['"]([-_\w\/]+)['"] *)?>/


/**
 * A basic Vite plugin that adds a <template layout="name"> syntax to Vite SFCs.
 * It must be used before the Vue plugin.
 */
export default (layouts: string = '@/views/layouts/'): Plugin => ({
	name: PLUGIN_NAME,
	transform: (code: string) => {
		if (!TEMPLATE_LAYOUT_REGEX.test(code)) {
			return
		}

		const isTypeScript = /lang=['"]ts['"]/.test(code)

		return code.replace(TEMPLATE_LAYOUT_REGEX, (_, layoutName) => `
			<script${isTypeScript ? ' lang="ts"' : ''}>
			import layout from '${layouts}${layoutName ?? 'default'}.vue'
			export default { layout }
			</script>
			<template>
		`)
	},
})

vite.config.ts:

import { defineConfig } from 'vite'
import laravel from 'vite-plugin-laravel'
import vue from '@vitejs/plugin-vue'
import inertia from './resources/scripts/vite/inertia-layout'


export default defineConfig({
	plugins: [
		inertia(),
		vue(),
		laravel(),
	],
})

main.ts:

import { createApp, h } from 'vue'
import { createInertiaApp, Head, Link } from '@inertiajs/inertia-vue3'
import { importPageComponent } from '@/scripts/vite/import-page-component'

createInertiaApp({
	resolve: (name) => importPageComponent(name, import.meta.glob('../views/pages/**/*.vue')),
	setup({ el, app, props, plugin }) {
		createApp({ render: () => h(app, props) })
			.use(plugin)
			.component('InertiaHead', Head)
			.component('InertiaLink', Link)
			.mount(el)
	},
})

Arturexe avatar May 04 '22 16:05 Arturexe

I'm experiencing same thing, but with different approach.

I'm using Nested Layouts:

  • BaseLayout.vue
    • GuestLayout.vue
    • AuthenticatedLayout.vue

Importing the layout like this:

createInertiaApp({
  title: (title) => `${title} - ${appName}`,
  resolve: (name) => {
    return resolvePageComponent(`./Pages/${name}.vue`, import.meta.glob('./Pages/**/*.vue')).then(
      (page) => {
        page.default.layout = name.startsWith('Partner/Company')
          ? [BaseLayout, AuthenticatedLayout]
          : [BaseLayout, GuestLayout];
        return page;
      }
    );
  },
...

Styling in GuestLayout.vue also affects AuthenticatedLayout.vue, which it shouldn't. HTML and Scripts seems to be splitted fine, and does not run on both Layouts.

Edit: I just noticed that, when i'm importing the layout manually, it still persists the CSS from a layout when going from e.g. GuestLayout to AuthenticatedLayout. However, the CSS is removed when doing a refresh.

madsh93 avatar Jul 25 '22 11:07 madsh93

Same issue. I've temporarily solved it by importing it using the ?inline vite directive, then manually rendering it using the component tag. Here's my Layout.vue file.

<template>
	<component :is="'style'">{{ css }}</component>
	
	<main>
		<slot />
	</main>
</template>

<script>

import css from "@/../scss/app.scss?inline";

export default {
	data(){
		return {
			css: css,
		}
	},
}
</script>

mixmav avatar Jul 29 '22 10:07 mixmav

@mixmav Great idea. But it is still a bit too hacky for me.

madsh93 avatar Aug 02 '22 12:08 madsh93

in my case the easiest approach is:

import style from "../../css/app.css?inline";

 <style>{style}</style>

yeasinsiam avatar Dec 30 '22 04:12 yeasinsiam

@reinink this seems like a glaring bug... is this the intended behavior?

mixmav avatar Mar 12 '23 17:03 mixmav

Hey! Thanks so much for your interest in Inertia.js and for sharing this issue/suggestion.

In an attempt to get on top of the issues and pull requests on this project I am going through all the older issues and PRs and closing them, as there's a decent chance that they have since been resolved or are simply not relevant any longer. My hope is that with a "clean slate" me and the other project maintainers will be able to better keep on top of issues and PRs moving forward.

Of course there's a chance that this issue is still relevant, and if that's the case feel free to simply submit a new issue. The only thing I ask is that you please include a super minimal reproduction of the issue as a Git repo. This makes it much easier for us to reproduce things on our end and ultimately fix it.

Really not trying to be dismissive here, I just need to find a way to get this project back into a state that I am able to maintain it. Hope that makes sense! ❤️

reinink avatar Jul 28 '23 01:07 reinink