Skip to content
Twilight React Playground
ثيم رائدaren

attrs, mergeAttrs (Next.js)

functionAdvanced

The Next.js copies of attrs and mergeAttrs: descriptors in, htmlAttrs and bodyAttrs out, for a root layout that cannot call hooks.

import { attrs, mergeAttrs } from '@salla.sa/twilight-theme-engine/nextjs';

In plain words

In a Next.js App Router project the root layout.tsx renders <html> and <body> as a Server Component, a component that runs only on the server and cannot call hooks. Attributes are worked out there from the route's parameters and passed through attrs().

Both functions behave exactly like their /tanstack namesakes; only the import path differs.

Signature

function attrs(descriptor: DocumentElementDescriptor): {
  htmlAttrs: Record<string, string>;
  bodyAttrs: Record<string, string>;
}

function mergeAttrs(...descriptors: DocumentElementDescriptor[]): {
  htmlAttrs: Record<string, string>;
  bodyAttrs: Record<string, string>;
}

Example

app/[locale]/layout.tsx (Next.js)
import type { ReactNode } from 'react';
import { attrs } from '@salla.sa/twilight-theme-engine/nextjs';

export default async function RootLayout({
  children,
  params,
}: {
  children: ReactNode;
  params: Promise<{ locale: string }>;
}) {
  const { locale } = await params;
  const { htmlAttrs, bodyAttrs } = attrs({
    html: { lang: locale, dir: locale === 'ar' ? 'rtl' : 'ltr' },
    body: { class: 'antialiased' },
  });

  return (
    <html {...htmlAttrs}>
      <body {...bodyAttrs}>{children}</body>
    </html>
  );
}

How it behaves

  • attrs has its own copy in src/nextjs/document-class.ts with the same rules; mergeAttrs is the shared mergeDocumentDescriptors. That file also defines mergeNextjsDocumentAttrs, which is not exported.

  • The rules and live demos are on attrs and mergeAttrs.

Related

Source and docs