Inclusive development practices in Next.js projects
In Part 1 of this series, we explored how Next.js provides React developers a strong starting point for accessibility (A11Y), with built-in features such as semantic routing, head management, and linting. Here in Part 2, we’ll go beyond the framework to real-world development practices and demonstrate how tools like axe DevTools can help you validate real-world usability.
By “inclusive development practices,” we mean modern development techniques that ensure that applications are built accessible from the start—integrated into the design and code, rather than bolted on later or left for post-production fixes.
We’ll cover practical ways to implement accessibility at the code level, including proper use of semantic HTML, alt text, ARIA roles, and keyboard support. Below are some best practices and code examples to help you develop apps that are inclusive by design.
Create with semantic HTML
Swapping a generic <div> for a more meaningful element such as <header>, <nav>, <main>, or <section> is a simple way to improve accessibility without adding complexity. These tags help screen readers and other assistive technologies understand how your page is structured, making it easier for users to navigate.
Semantic markup increases accessibility and also makes your code cleaner and easier to maintain.
<nav>
<ul>
<li>
<Link href="/">Home Page</Link>
</li>
<li>
<Link href="/about">About Us</Link>
</li>
</ul>
</nav>
By choosing the right element for the job, you support better experiences for everyone without having to write extra code.
Ensure keyboard accessibility
Make sure every interactive element in your app functions properly for keyboard users, from navigating links to activating buttons. This means managing focus properly, maintaining a logical tab order, and using event handlers that support both mouse and keyboard input.
Native HTML elements such as <button> already support keyboard interactions out of the box. But if you’re building a custom interactive component (such as a <div> acting as a button), you need to manually support keyboard events like Enter, Space, or even arrow keys for navigation.
Here’s an example of how to make a custom component keyboard-accessible:
'use client'
import { useRef } from 'react';
export const MyComponent = () => {
const divRef = useRef(null);
const handleClick = () => {
// Your click handler code here
};
const handleKeyDown = (event) => {
if (event.key === 'Enter' || event.key === ' ') {
event.preventDefault();
divRef.current.click();
} else if (event.key === 'ArrowLeft') {
// Handle left arrow navigation
} else if (event.key === 'ArrowRight') {
// Handle right arrow navigation
}
};
return (
<div
ref={divRef}
role="button"
tabIndex={0}
onClick={handleClick}
onKeyDown={handleKeyDown}
>
Learn More
</div>
);
};
This pattern ensures keyboard users get the same interactive experience as mouse users—especially when you’re building components from scratch.
Leverage ARIA roles and attributes
When semantic HTML doesn’t cover a specific use case, you can fill in the gaps with ARIA roles and attributes. Use them thoughtfully—they’re powerful tools but not substitutes for semantic markup. ARIA, when implemented poorly, can be a barrier rather than a path to accessibility.
<nav>
<ul role="menu">
<li role="menuitem">
<Link href="/">Home Page</Link>
</li>
<li role="menuitem">
<Link href="/about">About Us</Link>
</li>
</ul>
</nav>
By applying roles such as menu and menuitem, you give screen readers the context they need to interpret complex or custom navigation structures. Just remember: the best accessibility often starts with native HTML. Use ARIA when needed as a complement, not as a replacement.
Add alt text for media
You can make your content far more inclusive simply by adding descriptive text to your images and captions to your media. Every image should include an alt attribute that communicates its purpose. If the image is decorative, use alt=”” to let assistive technologies skip it.
<Image
src="/wilson.jpg"
width={450}
height={450}
alt="a wilson volleyball with a red handprint with a face on it"
/>
For videos or audio, you can include captions or subtitles to support users who are deaf or hard of hearing:
<video controls>
<source src="/helloWorld.mp4" type="video/mp4" />
<track kind="subtitles" src="/subtitles-en.vtt" label="English" />
</video>
By taking these simple steps, you’re actively making your content more accessible to everyone, regardless of how they experience the web.
Integrate localization
If your audience spans multiple languages or regions, Next.js makes localization easier with built-in support for automatic locale-based routing and language negotiation.
By integrating a localization library such as next-i18next or react-intl, you can deliver content that respects your users’ language preferences and make your app feel more human in the process.
Focus on performance and accessibility together
Performance and accessibility go hand in hand, and with features such as server-side rendering (SSR), automatic code splitting, and route-based prefetching, you can use Next.js to ensure your app loads faster and responds smoothly.
By optimizing performance, you’re creating an app that is easier to navigate for users of assistive technology and those with slower network connections. Building with both speed and inclusivity in mind helps create a better experience for everyone.
Test with accessibility tools
You can catch many common accessibility issues early by using automated testing tools. Libraries such as React Testing Library with Jest make it easy to validate accessible patterns in your components:
import { render, screen } from '@testing-library/react';
import MyComponent from './MyComponent';
test('needs to be accessible', () => {
render(<MyComponent />);
expect(screen.getByRole('button')).toBeVisible();
});
Building accessibility into your test suite means it’s part of your everyday workflow, making it easier to catch issues early and deliver more inclusive features with confidence.
Write tests with axe DevTools for browser JavaScript
If you’re using axe DevTools for Web, you can go even further by programmatically testing for accessibility violations within your browser-based JavaScript tests.
Once you’ve installed and initialized the axe DevTools browser package, you can scan any rendered component for accessibility issues. Here’s a basic example:
test('Component has no accessibility violations', (done) => {
const { container } = render(<Badcomp />);
axeDevTools.run(container, (err, results) => {
expect(results.violations.length).toBe(0);
done();
});
});
This test uses the run() method from @axe-devtools/browser to scan the rendered DOM and verify that there are no violations. It’s a fast, reliable way to enforce accessibility standards across your component library or app.
Need a full example? Here’s a full test file to get started:
import React from 'react';
import Badcomp from '../components/badcomp';
import { render } from '@testing-library/react';
import axeDevTools from '@axe-devtools/browser';
import "babel-polyfill";
describe('Bad Component', () => {
beforeEach((done) => {
axeDevTools.init('wcag2', () => done());
});
test('Component has no accessibility violations, w/ attest reporter', (done) => {
const { container } = render(<Badcomp />);
axeDevTools.run(container, (err, results) => {
expect(results.violations.length).toBe(0);
done();
});
});
});
Once you’ve got the basics in place, you can expand your tests with custom rules, integrate reporting, or work directly with the results object for deeper insights.
Use the axe DevTools Extension
Even with Next.js, some accessibility issues will slip through, especially the trickier, context-specific ones that frameworks can miss. That’s where the axe DevTools Extension can help.
By adding it to your browser, you can scan your pages, spot issues visually, and zero in on exactly what needs fixing—all powered by axe-core, the engine trusted by industry leaders.
You can catch most common accessibility issues right out of the box. And when you’re ready, you can upgrade to axe DevTools Pro and get AI-powered features including Intelligent Guided Tests, which further boost your coverage. You’ll also benefit from features such as component-level testing, exportable reports, Jira integration, and more.
Make digital accessibility part of your workflow from day one
When you design with accessibility in mind from the outset, you make your product usable for everyone, including people with disabilities. You also avoid the cost and complexity of retrofitting later, when fixes are harder and less effective.
This early, proactive approach is part of a broader development strategy known as shifting left, which means moving digital accessibility testing earlier in your workflow, right alongside your other quality checks. In our next article, we’ll delve into what shifting left means for digital accessibility and how it can transform your development process.
This is why Next.js offers such a solid start for React developers to build upon. It encourages developers to add accessibility to their repertoire. Whether it’s linting semantic HTML, announcing route changes, or managing metadata properly, the framework gives you guardrails to build more inclusively from the start.
Yet frameworks don’t build apps—developers do. As a developer, you have the ability and the opportunity to shape the experiences of your end-users. Next.js provides a solid foundation, and tools like the axe DevTools Extension enable you to build on that foundation, ensuring that you’re getting accessibility right, while backed by decades of proven best practices.
At Deque, our mission is to help create a digital world that works for everyone. If you’ve made it this far, you’re already part of that mission. So keep building, keep testing, and keep pushing to make the web more inclusive for all.