Skip to content
bitboss-ui3.0.0-beta.64

Nuxt

Configure the built-in Nuxt module, styles in the server render, icons, variants, and migration from the legacy Nuxt package.

On this page

There is no separate package to install for Nuxt. The module is an export of the library itself, and this documentation site runs on it. A Tailwind v4 app needs one option:

nuxt.config.ts
import { TAILWIND_V4_LAYERS } from 'bitboss-ui/nuxt';

export default defineNuxtConfig({
    modules: ['bitboss-ui/nuxt'],
    bitboss: {
        cssLayers: TAILWIND_V4_LAYERS,
    },
});

The module puts the stylesheet in the server render itself, so the first paint is styled, and turns the browser injection off. With cssLayers it sits in a layer below your utilities; without, it sits above your CSS where the injection put it. You add no css entry and no @layer line. Plain CSS or Tailwind v3: drop the option and read Installation.

The module ships with the library

bitboss-ui/nuxt pushes the library's Vite plugin into the Nuxt build and adds the runtime plugin. With autoImport on, it also registers the components and composables with Nuxt's auto-imports. Its config key is bitboss, and it takes the Vite plugin's options plus the Nuxt-only autoImport. A project moving between Vite and Nuxt moves its config across unchanged.

Leave autoImport off, which is the default. Explicit imports keep vueCompilerOptions.checkUnknownComponents able to fail the build on a mistyped or removed Bb* tag. Turn it on to move an app off bitboss-nuxt without adding every import in the same commit.

true registers the components and the composables, BbForm, useBbFormContext and useBbFormField included; { composables: false } keeps only the components. It never shadows a component of your own with the same name: a ~/components/BbButton.vue keeps winning. It also never registers the library's useId, because Nuxt already auto-imports Vue's. Import the library's explicitly if you want it.

The module mounts no component for you. confirm(), toast() and on-demand dialogs render in one <BbHost />, and in Nuxt that goes in app.vue, beside the layout:

app.vue
<template>
    <NuxtLayout>
        <NuxtPage />
    </NuxtLayout>
    <BbHost />
</template>

<script setup lang="ts">
import { BbHost } from 'bitboss-ui';
</script>

It renders nothing on the server, so it hydrates without a mismatch. Nuxt shows error.vue in place of app.vue, so an error page that fires a toast or a confirm mounts its own <BbHost />. This site's app.vue is the one above.

Options in the bitboss key

The bitboss key takes every option of bitbossUi(). The ones most apps set are in the options list on Installation, with their defaults. A few read differently in Nuxt:

  • cssLayers layers the stylesheet the module adds to the server render, as above.
  • injectStyles goes false only when you list bitboss-ui/styles.css in css yourself.
  • iconDir resolves from app/, see below.
  • locale is English by default, and an Italian app sets locale: 'it'. For a per-request locale, call useBbConfig().setConfig({ locale }) inside nuxtApp.vueApp.runWithContext() from a plugin of your own.
  • nuxtLinkName names the component that renders router links, 'NuxtLink' by default.
  • pt and ptScopes take the same maps as in Vite. Import definePt from bitboss-ui/nuxt, see Passthrough.

Two options write files on your machine when the dev server starts, and each logs what it changed. vscodeSettings is on by default: it edits .vscode/settings.json, keeping comments, formatting and key order, and vscodeSettings: false stops it. mcp is off by default: set it to true and dev writes the server into .mcp.json, .cursor/mcp.json and .vscode/mcp.json, leaving any other servers alone. That is the registration npx bitboss-ui ai-init does, except that it never installs anything, so run ai-init once for the server's peers. See AI agents.

Registering variants

The variant registries are plugin options too, so in Nuxt they live in the same bitboss block:

nuxt.config.ts
bitboss: {
    buttonVariants: ['success'],
}

A list adds to the built-in names. Leaving one out does not remove it.

Registering the name makes it type-check once the plugin has written app/bitboss-ui.d.ts (commit it). npx bitboss-ui check writes the same file without starting the dev server. You still own the variant's CSS; see Design language, which covers both halves and what happens if you do only one.

The registries are buttonVariants, badgeVariants, alertVariants, indicatorVariants, toastVariants, confirmVariants, dropdownItemVariants, tooltipVariants, inputVariants for the text-like form controls, and tabsVariants for BbTabs.

Icons in Nuxt

iconDir is resolved from the Vite root, which in Nuxt 4 is app/: leave the default './assets/icons', which already means app/assets/icons (Installation shows what './app/assets/icons' breaks). The build also scans the whole project for provider names (lucide:save), not only app/, so a name written in server/ or content/ still ships. Everything else about naming, the build-time literal scan, and where components already accept an icon is in Icons.

Prerendering and SSR

The components server-render, and the module bundles the library into the server build for you. This site runs nuxt generate with failOnError enabled and does not wrap library components in ClientOnly.

The server cannot measure the screen, so it renders for ssrViewportWidth (1024 by default): a mobile visitor's first paint is desktop-shaped and adjusts once the client measures. If most of your readers are on phones, set it lower in the bitboss key, for example ssrViewportWidth: 768.

Two things to know when a page fetches its own options:

  • prefill: true on BbSelect runs your provider once, in onServerPrefetch. The rows travel to the browser in the Nuxt payload, so the selection is already in the server-rendered HTML and the client does not fetch again. The module installs the adapter that carries them. Return plain, serializable rows: objects, arrays and dates, not class instances or ORM models.
  • The default 'interaction' defers the load to the client, which is usually what you want and is why it is the default.

Only that first load travels. A search the user types, a dependencies change and a page change in BbTable all fetch in the browser. In dev, a warning that useId() differs between server and client means an onServerPrefetch of your own, which Nuxt strips from the production client build. Load that data with useAsyncData instead.

Fetching data has the full matrix.

On a static site the reader's theme is not knowable at build time. Apply the class from an inline script in head before hydration, or the first paint flashes the wrong theme. Design tokens explains why color-scheme follows the class rather than the OS preference.

If you have bitboss-nuxt installed

@bitboss-dev/bitboss-ui-nuxt is a separate, v2-era package. It predates this module, and its API shows it: unprefixed icon names, a components.validation switch, and BaseButton rather than BbBaseButton.

Migrating off it is mostly deletion:

  1. Remove @bitboss-dev/bitboss-ui-nuxt from modules and from package.json, and add 'bitboss-ui/nuxt'.
  2. Move icons.path to iconDir, styles.enabled to injectStyles, link.name to nuxtLinkName and components.requiredAsterisk to requiredAsterisk, which now defaults to true. Drop icons.enabled, styles.index, link.enabled and logLevel: v3 has no equivalent.
  3. Drop components and composables. Import what each template uses, or set autoImport: true until you have added the imports. Its default flipped: the old module auto-imported, this one does not.
  4. Prefix your own icon names with local:, which v3 requires. See Icons.
  5. Drop components.validation. The form controls validate inside a BbForm with no switch; see Validated forms.

Then work through Migrating from v2, because a project on that module is on v2 of the library as well.