gp-grid-logo

Nuxt 4 Integration

Use gp-grid in Nuxt 4 with global styles, SSR-safe rendering, server data sources, optional client-only rendering, and stable grid sizing for production apps.

gp-grid works with Nuxt 4 through the @gp-grid/vue package. The component is SSR-safe: it renders its initial structure on the server and starts DOM measurement, event listeners, and Web Workers only after it mounts in the browser.

Installation

Install the Vue package:

pnpm add @gp-grid/vue

Register the stylesheet globally in nuxt.config.ts:

export default defineNuxtConfig({
  css: ["@gp-grid/vue/dist/styles.css"],
});

No Nuxt module or Vite transpilation configuration is required.

Basic Usage

Nuxt 4 places application pages under app/pages by default:

<!-- app/pages/grid.vue -->
<script setup lang="ts">
import { GpGrid, type ColumnDefinition } from "@gp-grid/vue";

interface Person {
  id: number;
  name: string;
}

const columns: ColumnDefinition[] = [
  { field: "id", cellDataType: "number", width: 80, headerName: "ID" },
  { field: "name", cellDataType: "text", width: 200, headerName: "Name" },
];

const { data } = await useFetch<Person[]>("/api/users", {
  default: () => [],
});
</script>

<template>
  <div style="width: 100%; height: 500px">
    <GpGrid
      :columns="columns"
      :row-data="data"
      :row-height="36"
      :initial-height="500"
    />
  </div>
</template>

The parent must have an explicit height because the grid fills its container. initial-width and initial-height can provide stable dimensions for the server render; after hydration, ResizeObserver measures the actual container.

SSR and Client-Only Rendering

<ClientOnly> is not required for the GpGrid component. Use it when the grid has no useful server-rendered content, or when its surrounding setup depends on browser-only APIs:

<template>
  <ClientOnly>
    <div style="width: 100%; height: 500px">
      <GpGrid :columns="columns" :row-data="rows" :row-height="36" />
    </div>

    <template #fallback>
      <div style="width: 100%; height: 500px" aria-busy="true">
        Loading grid...
      </div>
    </template>
  </ClientOnly>
</template>

For a reusable client-only wrapper, place the grid in a component whose name ends in .client.vue, such as app/components/UserGrid.client.vue.

Client Data Sources During SSR

Passing row-data is the simplest SSR-safe option because GpGrid creates its client data source after mounting.

If you create a client data source in <script setup>, disable Web Workers for the server-rendered instance:

<script setup lang="ts">
import {
  GpGrid,
  createClientDataSource,
  type ColumnDefinition,
} from "@gp-grid/vue";

const columns: ColumnDefinition[] = [
  { field: "id", cellDataType: "number", width: 80 },
  { field: "name", cellDataType: "text", width: 200 },
];

const rows = [
  { id: 1, name: "Giovanni" },
  { id: 2, name: "Luca" },
];

const dataSource = createClientDataSource(rows, { useWorker: false });
</script>

<template>
  <div style="height: 500px">
    <GpGrid
      :columns="columns"
      :data-source="dataSource"
      :row-height="36"
    />
  </div>
</template>

To retain worker-backed sorting, construct the data source inside a .client.vue component so its setup runs only in the browser.

Server-Side Data with API Routes

For large datasets, connect the grid to a Nuxt server route with createServerDataSource.

API Route

// server/api/grid-data.post.ts
import { defineEventHandler, readBody } from "h3";

export default defineEventHandler(async (event) => {
  const { range, sort, filter } = await readBody(event);

  // Query your database. endRow is exclusive.
  const results = await db.query({
    offset: range.startRow,
    limit: range.endRow - range.startRow,
    orderBy: sort,
    where: filter,
  });

  return {
    rows: results.data,
    totalRows: results.total,
  };
});

Grid Component

<script setup lang="ts">
import {
  GpGrid,
  createServerDataSource,
  type ColumnDefinition,
} from "@gp-grid/vue";

const columns: ColumnDefinition[] = [
  { field: "id", cellDataType: "number", width: 80, headerName: "ID" },
  { field: "name", cellDataType: "text", width: 200, headerName: "Name" },
];

const dataSource = createServerDataSource(async (request) => {
  return await $fetch("/api/grid-data", {
    method: "POST",
    body: request,
  });
});
</script>

<template>
  <div style="height: 500px">
    <GpGrid
      :columns="columns"
      :data-source="dataSource"
      :row-height="36"
    />
  </div>
</template>

The request includes the visible row range together with the active sort and filter models. Return the requested rows and the total number of matching rows.

Dark Mode with Nuxt Color Mode

When using @nuxtjs/color-mode, pass its state to the dark-mode prop:

<script setup lang="ts">
import { GpGrid } from "@gp-grid/vue";

const colorMode = useColorMode();
const isDark = computed(() => colorMode.value === "dark");
</script>

<template>
  <div style="height: 500px">
    <GpGrid
      :columns="columns"
      :row-data="rows"
      :row-height="36"
      :dark-mode="isDark"
    />
  </div>
</template>

If the server cannot determine the user's preferred color mode, wrap only the theme-dependent grid instance in <ClientOnly> or provide a matching fallback to avoid a visible theme change during hydration.

On this page