ui icon indicating copy to clipboard operation
ui copied to clipboard

Incompatibility with Inertia SSR Server

Open miguilimzero opened this issue 2 months ago • 3 comments

Is this bug related to Nuxt or Vue?

Vue (Inertia in particular)

Package

v4.x

Version

v4.0.1

Reproduction

The UI library is currently incompatible with the Inertia SSR server: https://inertiajs.com/server-side-rendering.

The compiled SSR file tries to alter the document.head which is not possible in SSR:

  if (nuxtApp.isHydrating && !nuxtApp.payload.serverRendered) {
    const style = document.createElement("style");
    style.innerHTML = root.value;
    style.setAttribute("data-nuxt-ui-colors", "");
    document.head.appendChild(style);
    headData.script = [{
      innerHTML: "document.head.removeChild(document.querySelector('[data-nuxt-ui-colors]'))"
    }];
  }

It is currently trying to do that as the payload.serverRendered is set to false in the useNuxtApp() function in the compiled file:

 function useNuxtApp() {
  return {
    isHydrating: true,
    payload: { serverRendered: false },
    hooks,
    hook: hooks.hook
  };
}

Changing the value manually to true seems to fix the issue, so I believe it is just a matter of detecting if it is being compiled to Inertia SSR or not.

miguilimzero avatar Oct 17 '25 23:10 miguilimzero

I managed to automate this change by using this command on my deploy script:

sed -i -E 's/(serverRendered[[:space:]]*:[[:space:]]*)false/\1true/' bootstrap/ssr/ssr.js

And the combination of the following commands for testing:

npm run build && sed -i -E 's/(serverRendered[[:space:]]*:[[:space:]]*)false/\1true/' bootstrap/ssr/ssr.js && php artisan inertia:start-ssr

However the CSS of the SSR version is not correct as its missing many -ui- variables. It seems to be happening because the SSR doesn't have the style tag with the id nuxt-ui-colors with the missing variables.

miguilimzero avatar Oct 21 '25 04:10 miguilimzero

Icons are also not present in the SSR version.

miguilimzero avatar Oct 21 '25 04:10 miguilimzero

For the styles, the workaround I found is in ssr.ts:

import { createInertiaApp } from '@inertiajs/vue3';
import createServer from '@inertiajs/vue3/server';
import ui from '@nuxt/ui/vue-plugin';
import { createHead, renderSSRHead } from '@unhead/vue/server';
import { resolvePageComponent } from 'laravel-vite-plugin/inertia-helpers';
import { createSSRApp, DefineComponent, h } from 'vue';
import { renderToString } from 'vue/server-renderer';
import PersistentLayout from './layouts/PersistentLayout.vue';

createServer(
    (page) => {
        const head = createHead();
        return createInertiaApp({
            page,
            render: renderToString,
            resolve: (name) =>
                resolvePageComponent(
                    `./pages/${name}.vue`,
                    import.meta.glob<DefineComponent>('./pages/**/*.vue'),
                ),
            setup: ({ App, props, plugin }) =>
                createSSRApp({ render: () => h(App, props) })
                    .use(plugin)
                    .use(head)
                    .use(ui),
        }).then(async (app) => {
            const payload = await renderSSRHead(head);
            app.head.push(payload.headTags);
            return app;
        });
    },
    { cluster: true },
);

This solves the color issue and allows you to use Unhead in your project instead of Inertia's Head (Unhead is better).

For the icons, the only way I found is to use unplugin-icons and pass the icon component instead of its name everywhere it's needed in UIcon UButton UCard and so on... (see https://github.com/nuxt/ui/pull/4766)

y-l-g avatar Nov 03 '25 14:11 y-l-g