Nuxt
Nuxt's server runs on Nitro, which exposes a render:response hook. Register the adapter from a Nitro server plugin so you control import order — the DOM shim must be imported there first, before any component module is evaluated:
// server/plugins/element-ssr.js
import "@webtides/element-js-ssr-renderer/dom-shim";
import { elementSSR } from "@webtides/element-js-ssr-renderer/nuxt";
import Button from "@webtides/element-library/button";
export default defineNitroPlugin((nitroApp) => {
nitroApp.hooks.hook(
"render:response",
elementSSR({
resolve: [
{ "el-button": Button }, // eager element-library components
// Nitro isn't Vite, so `import.meta.glob` is unavailable — a hand-written
// (or generated) lazy catalog goes straight into `resolve`, no wrapper:
{ "x-counter": () => import("../../elements/x-counter.js") },
],
}),
);
});elementSSR runs on renderToString, so it takes the same sources as everywhere else (see Resolving components). The render:response hook hands you a plain response object ({ body, headers, statusCode }) rather than a web Response and you mutate it in place, so the adapter wraps response.body, runs it through the same internal transformHtmlResponse kernel the Astro adapter uses, and writes the transformed HTML back.
Author components as plain HTML in your pages, and load each component's define on the client (e.g. from a .client.js Nuxt plugin).
Tell Vue your tags are custom elements
Vue's template compiler warns about hyphenated tags it can't resolve as Vue components. Mark them as native custom elements in nuxt.config.ts:
export default defineNuxtConfig({
vue: { compilerOptions: { isCustomElement: (tag) => tag.includes("-") } },
});Runnable example
A complete, runnable version lives in examples/nuxt/ — a Nuxt app composing element-library components (an eager static catalog) with its own (a generated lazy catalog), covering both the shadow (DSD) and light-DOM paths.
cd examples/nuxt && npm install && npm run devImport-order gotcha
Nuxt bundles the server (Nitro/rollup), so a bundler may hoist element-js' import above the inlined DOM-shim side effect. The example fixes it with nitro.externals.inline for the element-js packages. See Installation.
No import.meta.glob on the server
import.meta.glob is a Vite feature, but Nuxt's server runs on Nitro (rollup), so the lazy catalog is a generated { 'x-counter': () => import('…') } map (via element-js-ssr-renderer catalog) rather than import.meta.glob('./elements/*.js'). Same behavior — a module is only imported when its tag appears on the page.