How to use axe-core with NextJS app router

29 Views Asked by At

Given the changes introduced with the app router in NextJS, is there a way to integrate axe-core with a NextJS app?

The old way mentioned here for example is no longer suitable for an app router:

import type React from 'react'
 
export const reportAccessibility = async (
  App: typeof React,
  config?: Record<string, unknown>
): Promise<void> => {
  if (process.env.NODE_ENV !== 'production') {
    const axe = await import('@axe-core/react')
    const ReactDOM = await import('react-dom')
 
    axe.default(App, ReactDOM, 1000, config)
  }
}
 
export default reportAccessibility

In the old pages router we used to do:

import React from 'react'
import ReactDOM from 'react-dom/client'
import App from './App'
import reportAccessibility from './reportAccessibility'
 
const root = ReactDOM.createRoot(
  document.getElementById('root') as HTMLElement
)
root.render(
  <React.StrictMode>
    <App />
  </React.StrictMode>
)
 
reportAccessibility(React)

Which is no longer relevant. Unfortunately, I could not find anything in axe-core or NextJS docs about this. Has anyone figured it out?

1

There are 1 best solutions below

0
aghidini On

You can create a client component importing and initializing @axe-core/react and include this component in your root layout file.

This is how I implemented the component Axe.tsx:

'use client';

import React from 'react';

const Axe: React.FC = () => {
  if (typeof window !== 'undefined' && process.env.NODE_ENV !== 'production') {
    Promise.all([import('@axe-core/react'), import('react-dom')]).then(
      ([axe, ReactDOM]) => axe.default(React, ReactDOM, 1000)
    );
  }
  return null;
};

export default Axe;

Then simply use Axe component in your main app/layout.tsx file:

import React from 'react';
import Axe from '@/components/a11y/Axe';

export default async function RootLayout({
  children,
}: {
  children: React.ReactNode;
}) {
  return (
    <html>
      <body>
        <Axe />
        {children}
    </html>
  );
}