Next.js is a powerful React framework that supports both server-side rendering (SSR) and client-side rendering. While web components have traditionally faced challenges with SSR in Next.js, the DÁP Design System can be successfully integrated using client-side rendering strategies.
- Next.js 13+ with App Router: Full support with client-side rendering
- Next.js 14+ with React 18: Recommended for optimal performance
- Next.js 15+ with React 19: Native web component support (experimental)
Web components require browser APIs that aren't available during server-side rendering. The recommended approach is to use client-side rendering with the 'use client'
directive for components that use the DÁP Design System.
Install the DÁP Design System package:
npm install dap-design-system
The App Router (Next.js 13+) is the recommended approach for new projects. Here's how to set up the DÁP Design System:
Create a client-side component to load the design system components:
'use client'
import { ReactNode, useEffect } from 'react'
export default function ClientApplication({
children,
}: {
children: ReactNode
}) {
useEffect(() => {
async function getComponents() {
await import('dap-design-system/dist/dds')
}
getComponents()
}, [])
return children
}
Update your root layout to include the design system styles, fonts, and client wrapper:
import localFont from 'next/font/local'
import { ReactNode, Suspense } from 'react'
import 'dap-design-system/dist/light.theme.css'
import ClientApplication from '@/app/clientApplication'
const inter = localFont({
src: '[path to your font, check the Typography page]',
display: 'swap',
weight: '500 700',
style: 'normal',
declarations: [
{ prop: 'font-feature-settings', value: "'liga' 1,'calt' 1,'ss02' 1" },
{ prop: 'font-variation-settings', value: "'slnt' 0" },
{ prop: 'font-optical-sizing', value: 'auto' },
],
})
export default async function RootLayout({
children,
}: Readonly<{
children: ReactNode
}>) {
return (
<html lang="en">
<body className={inter.className}>
<ClientApplication>
<Suspense fallback={<Loading />}>
<main className="main" id="root">
{children}
</main>
</Suspense>
</ClientApplication>
</body>
</html>
)
}
After setup, you can use DÁP Design System components in your Next.js project. Remember to use the 'use client'
directive in components that use the design system.
'use client'
import { useState } from 'react'
export default function ContactForm() {
const [formData, setFormData] = useState({
name: '',
email: '',
message: ''
})
const handleInputChange = (e: CustomEvent, field: string) => {
setFormData(prev => ({
...prev,
[field]: e.detail?.value || ''
}))
}
const handleSubmit = (e: CustomEvent) => {
e.preventDefault()
console.log('Form submitted:', formData)
}
return (
<div className="max-w-md mx-auto p-6">
<dap-ds-typography variant="h2">
Contact Us
</dap-ds-typography>
<form ondds-submit={handleSubmit} className="space-y-4">
<dap-ds-input
label="Name"
placeholder="Your full name"
value={formData.name}
ondds-change={(e) => handleInputChange(e, 'name')}
required
/>
<dap-ds-input
label="Email"
type="email"
placeholder="your.email@example.com"
value={formData.email}
ondds-change={(e) => handleInputChange(e, 'email')}
required
/>
<dap-ds-textarea
label="Message"
placeholder="Tell us about your project..."
value={formData.message}
rows={4}
ondds-change={(e) => handleInputChange(e, 'message')}
required
/>
<dap-ds-button type="submit" variant="primary" className="w-full">
Send Message
</dap-ds-button>
</form>
</div>
)
}
For full TypeScript support in Next.js, add the type definitions to your project:
Create or update your global.d.ts
file in your project root:
declare namespace JSX {
interface IntrinsicElements {
'dap-ds-accordion': import('dap-design-system/dist/react-types').DapDSAccordionType
'dap-ds-accordion-group': import('dap-design-system/dist/react-types').DapDSAccordionGroupType
'dap-ds-avatar': import('dap-design-system/dist/react-types').DapDSAvatarType
'dap-ds-avatar-group': import('dap-design-system/dist/react-types').DapDSAvatarGroupType
'dap-ds-badge': import('dap-design-system/dist/react-types').DapDSBadgeType
'dap-ds-banner': import('dap-design-system/dist/react-types').DapDSBannerType
'dap-ds-breadcrumb': import('dap-design-system/dist/react-types').DapDSBreadcrumbType
'dap-ds-breadcrumb-item': import('dap-design-system/dist/react-types').DapDSBreadcrumbItemType
'dap-ds-button': import('dap-design-system/dist/react-types').DapDSButtonType
'dap-ds-calendar': import('dap-design-system/dist/react-types').DapDSCalendarType
'dap-ds-calendar-cell': import('dap-design-system/dist/react-types').DapDSCalendarCellType
'dap-ds-callout': import('dap-design-system/dist/react-types').DapDSCalloutType
'dap-ds-card': import('dap-design-system/dist/react-types').DapDSCardType
'dap-ds-card-actions': import('dap-design-system/dist/react-types').DapDSCardActionsType
'dap-ds-card-content': import('dap-design-system/dist/react-types').DapDSCardContentType
'dap-ds-card-image': import('dap-design-system/dist/react-types').DapDSCardImageType
'dap-ds-card-subtitle': import('dap-design-system/dist/react-types').DapDSCardSubtitleType
'dap-ds-card-title': import('dap-design-system/dist/react-types').DapDSCardTitleType
'dap-ds-checkbox': import('dap-design-system/dist/react-types').DapDSCheckboxType
'dap-ds-combobox': import('dap-design-system/dist/react-types').DapDSComboboxType
'dap-ds-command': import('dap-design-system/dist/react-types').DapDSCommandType
'dap-ds-command-group': import('dap-design-system/dist/react-types').DapDSCommandGroupType
'dap-ds-command-item': import('dap-design-system/dist/react-types').DapDSCommandItemType
'dap-ds-content-switcher': import('dap-design-system/dist/react-types').DapDSContentSwitcherType
'dap-ds-content-switcher-item': import('dap-design-system/dist/react-types').DapDSContentSwitcherItemType
'dap-ds-copybox-input': import('dap-design-system/dist/react-types').DapDSCopyBoxInputType
'dap-ds-datatable': import('dap-design-system/dist/react-types').DapDSDataTableType
'dap-ds-datepicker': import('dap-design-system/dist/react-types').DapDSDatePickerType
'dap-ds-divider': import('dap-design-system/dist/react-types').DapDSDividerType
'dap-ds-feedback': import('dap-design-system/dist/react-types').DapDSFeedbackType
'dap-ds-file-input': import('dap-design-system/dist/react-types').DapDSFileInputType
'dap-ds-file-input-list': import('dap-design-system/dist/react-types').DapDSFileInputListType
'dap-ds-file-input-list-item': import('dap-design-system/dist/react-types').DapDSFileInputListItemType
'dap-ds-form-label': import('dap-design-system/dist/react-types').DapDSFormLabelType
'dap-ds-icon': import('dap-design-system/dist/react-types').DapDSIconType
'dap-ds-icon-button': import('dap-design-system/dist/react-types').DapDSIconButtonType
'dap-ds-input': import('dap-design-system/dist/react-types').DapDSInputType
'dap-ds-input-group': import('dap-design-system/dist/react-types').DapDSInputGroupType
'dap-ds-label': import('dap-design-system/dist/react-types').DapDSLabelType
'dap-ds-link': import('dap-design-system/dist/react-types').DapDSLinkType
'dap-ds-list-item': import('dap-design-system/dist/react-types').DapDSListItemType
'dap-ds-modal': import('dap-design-system/dist/react-types').DapDSModalType
'dap-ds-notification-badge': import('dap-design-system/dist/react-types').DapDSNotificationBadgeType
'dap-ds-number-input': import('dap-design-system/dist/react-types').DapDSNumberInputType
'dap-ds-official-website-banner': import('dap-design-system/dist/react-types').DapDSOfficialWebsiteBannerType
'dap-ds-option-item': import('dap-design-system/dist/react-types').DapDSOptionItemType
'dap-ds-option-list': import('dap-design-system/dist/react-types').DapDSOptionListType
'dap-ds-overlay': import('dap-design-system/dist/react-types').DapDSOverlayType
'dap-ds-pager': import('dap-design-system/dist/react-types').DapDSPagerType
'dap-ds-password-input': import('dap-design-system/dist/react-types').DapDSPasswordInputType
'dap-ds-popup': import('dap-design-system/dist/react-types').DapDSPopupType
'dap-ds-radio-button': import('dap-design-system/dist/react-types').DapDSRadioButtonType
'dap-ds-radio-group': import('dap-design-system/dist/react-types').DapDSRadioGroupType
'dap-ds-rating': import('dap-design-system/dist/react-types').DapDSRatingType
'dap-ds-scroll-area': import('dap-design-system/dist/react-types').DapDSScrollAreaType
'dap-ds-search': import('dap-design-system/dist/react-types').DapDSSearchType
'dap-ds-select': import('dap-design-system/dist/react-types').DapDSSelectType
'dap-ds-sidenav': import('dap-design-system/dist/react-types').DapDSSideNavType
'dap-ds-sidenav-group': import('dap-design-system/dist/react-types').DapDSSideNavGroupType
'dap-ds-sidenav-item': import('dap-design-system/dist/react-types').DapDSSideNavItemType
'dap-ds-skip-link': import('dap-design-system/dist/react-types').DapDSSkipLinkType
'dap-ds-snackbar': import('dap-design-system/dist/react-types').DapDSSnackbarType
'dap-ds-snackbar-message': import('dap-design-system/dist/react-types').DapDSSnackbarMessageType
'dap-ds-spinner': import('dap-design-system/dist/react-types').DapDSSpinnerType
'dap-ds-stack': import('dap-design-system/dist/react-types').DapDSStackType
'dap-ds-switch': import('dap-design-system/dist/react-types').DapDSSwitchType
'dap-ds-tab': import('dap-design-system/dist/react-types').DapDSTabType
'dap-ds-tab-group': import('dap-design-system/dist/react-types').DapDSTabGroupType
'dap-ds-table': import('dap-design-system/dist/react-types').DapDSTableType
'dap-ds-table-cell': import('dap-design-system/dist/react-types').DapDSTableCellType
'dap-ds-table-header': import('dap-design-system/dist/react-types').DapDSTableHeaderType
'dap-ds-table-row': import('dap-design-system/dist/react-types').DapDSTableRowType
'dap-ds-textarea': import('dap-design-system/dist/react-types').DapDSTextareaType
'dap-ds-timeline': import('dap-design-system/dist/react-types').DapDSTimelineType
'dap-ds-timeline-item': import('dap-design-system/dist/react-types').DapDSTimelineItemType
'dap-ds-toc': import('dap-design-system/dist/react-types').DapDSTOCType
'dap-ds-toggle-button': import('dap-design-system/dist/react-types').DapDSToggleButtonType
'dap-ds-tooltip': import('dap-design-system/dist/react-types').DapDSTooltipType
'dap-ds-tray': import('dap-design-system/dist/react-types').DapDSTrayType
'dap-ds-typography': import('dap-design-system/dist/react-types').DapDSTypographyType
}
}
declare module 'dap-design-system/dist/dds.js'
Update your tsconfig.json
to include the type definitions:
{
"compilerOptions": {
"target": "es5",
"lib": ["dom", "dom.iterable", "es6"],
"allowJs": true,
"skipLibCheck": true,
"strict": true,
"forceConsistentCasingInFileNames": true,
"noEmit": true,
"esModuleInterop": true,
"module": "esnext",
"moduleResolution": "bundler",
"resolveJsonModule": true,
"isolatedModules": true,
"jsx": "preserve",
"incremental": true,
"plugins": [
{
"name": "next"
}
],
"baseUrl": ".",
"paths": {
"@/*": ["./src/*"],
"@/components/*": ["./src/components/*"]
}
},
"include": [
"next-env.d.ts",
"global.d.ts",
"**/*.ts",
"**/*.tsx",
".next/types/**/*.ts"
],
"exclude": ["node_modules"]
}
Next.js 15 with React 19 provides experimental native web component support. Here's how to leverage it:
/** @type {import('next').NextConfig} */
const nextConfig = {
experimental: {
// Enable React 19 features
reactCompiler: true,
},
transpilePackages: ['dap-design-system'],
}
module.exports = nextConfig
With React 19, you can use web components more naturally:
'use client'
import { useState } from 'react'
import 'dap-design-system/dist/dds.js'
import 'dap-design-system/dist/light.theme.css'
export default function React19Example() {
const [count, setCount] = useState(0)
return (
<div>
<dap-ds-typography variant="h3">
React 19 + Next.js 15 Integration
</dap-ds-typography>
<dap-ds-card>
<dap-ds-card-content>
<p>Count: {count}</p>
<dap-ds-button
ondds-click={() => setCount(c => c + 1)}
variant="primary"
>
Increment
</dap-ds-button>
</dap-ds-card-content>
</dap-ds-card>
</div>
)
}
'use client'
import { useEffect, useState } from 'react'
export default function ThemeProvider({ children }: { children: React.ReactNode }) {
const [theme, setTheme] = useState<'light' | 'dark'>('light')
useEffect(() => {
// Load theme-specific CSS
import(`dap-design-system/dist/${theme}.theme.css`)
}, [theme])
useEffect(() => {
// Set theme attribute on document
document.documentElement.setAttribute('data-theme', theme)
}, [theme])
return (
<div data-theme={theme}>
{children}
<dap-ds-button
variant="ghost"
ondds-click={() => setTheme(t => t === 'light' ? 'dark' : 'light')}
>
Toggle Theme
</dap-ds-button>
</div>
)
}
Hydration Mismatch: Web components render differently on server vs client
// Solution: Use dynamic imports with ssr: false
const ClientOnlyComponent = dynamic(() => import('./MyComponent'), {
ssr: false
})
TypeScript Errors: Missing type definitions
# Ensure global.d.ts is included in your tsconfig.json
npm run type-check
Styling Issues: CSS not loading properly
// Make sure to import theme CSS in your layout
import 'dap-design-system/dist/light.theme.css'
Ensure your build process handles web components correctly:
{
"scripts": {
"dev": "next dev",
"build": "next build",
"start": "next start",
"type-check": "tsc --noEmit"
}
}