How to make your Next.js website compatible with older browsers?

How to make your Next.js website compatible with older browsers?

Published on: October 26, 2024

Support for older web browsers is important when it comes to building websites. If the support isn’t implemented, some visitors who use older browsers may turn away from your website.

Next.js is a very popular React framework maintained by Vercel that provides static site generation (SSG) and server-side rendering (SSR). It is used to build websites, and web applications.

In this post, you will configure few settings in your Next.js website or web application to support older browsers. This post will assume you use Next.js 14 for your Next.js website or web application.

About browser compatibility

Browser compatibility refers to the ability of a web application to function correctly and consistently across different web browsers and their various versions. This includes ensuring that the application's layout, functionality, and performance are maintained regardless of the browser being used. Achieving browser compatibility involves addressing differences in how browsers interpret and render HTML, CSS, and JavaScript.

In the context of Next.js, browser compatibility means ensuring that your application works seamlessly on both modern browsers (like the latest versions of Chrome, Firefox, and Safari) and older browsers (like older versions of Chrome, Firefox, and Safari). This requires careful consideration of the features and technologies you use, as well as thorough testing to identify and fix any issues that arise.

While it may be tempting to focus solely on modern browsers, supporting older browsers is still important for several reasons:

  • Accessibility – not all users have access to the latest browsers or devices. By supporting older browsers, you ensure that your application is accessible to a wider range of users, including those in areas with limited internet access or those using older devices.
  • Market Share – although the usage of older browsers has declined, there are still significant numbers of users who rely on them. For example, as of 2021, Internet Explorer 11 still has a global market share of around 1%. Depending on your target audience, this could represent a significant number of potential users.
  • Enterprise Users – many businesses and organizations, particularly in industries like finance, healthcare, and government, still use older browsers due to legacy systems or security policies. Failing to support these browsers could limit your application's adoption in these sectors.
  • User Experience – providing a consistent and functional user experience across all browsers helps build trust and satisfaction among your users. A user who encounters a broken or poorly functioning application on an older browser may be less likely to return or recommend your application to others.
  • Inclusive Design – supporting older browsers is part of a broader commitment to inclusive design, which aims to create products that are accessible and usable by as many people as possible, regardless of their abilities or circumstances.

Setting up Next.js project

You can read a blog post about setting up a Next.js project that guides you through setting up a Next.js project.

Transpilation

Transpilation is the process of converting the source code written in a high-level programming language into code written in another high-level programming language. In the context of browser compatibility, the transpilation will occur from newer versions of ECMAScript (JavaScript) to older versions of ECMAScript to ensure compatibility with older browsers.

Next.js already uses SWC in its Next.js compiler to transpile JavaScript scripts. However, you will use SWC CLI additionally to transpile the scripts.

To transpile scripts in Next.js project during the build time, install the SWC CLI with this command:

npm install @swc/cli --save-dev

After installing SWC CLI, add the SWC CLI in the build script to the package.json file. The end result would look like this (modified from a Next.js application bootstraped with create-next-app):

{
  "name": "example-next-app",
  "version": "0.1.0",
  "private": true,
  "scripts": {
    "dev": "next dev",
    "build": "next build && swc .next/static/chunks/*.js .next/static/chunks/**/*.js -d . --include-dotfiles",
    "start": "next start",
    "lint": "next lint"
  },
  "dependencies": {
    "react": "^18",
    "react-dom": "^18",
    "next": "14.2.5"
  },
  "devDependencies": {
    "typescript": "^5",
    "@swc/cli": "^0.4.1-nightly.20240914",
    "@types/node": "^20",
    "@types/react": "^18",
    "@types/react-dom": "^18",
    "postcss": "^8",
    "tailwindcss": "^3.4.1",
    "eslint": "^8",
    "eslint-config-next": "14.2.5"
  }
}

After adding the SWC CLI command to the package.json file, create a .swcrc file in the root of the project with these contents:

{
  "jsc": {
    "keepClassNames": true,
    "target": "es2017"
  },
  "minify": true
}

This SWC configuration will make SWC transpile to the ES2017 syntax.

Polyfills

Polyfill is code that implements new functionality in the environment where the functionality isn’t supported. In terms of browser compatibility, it allows older browsers to use functionality native to newer browsers.

Next.js already injects widely used polyfills, such as for Fetch API, URL, or Object.assign(). You can view the documentation page for polyfills on Next.js. However, you can add custom polyfills, if you are using Pages Router. You can add the import for the polyfill on top of _app.jsx or _app.tsx file in the pages directory. Here is an example of the _app.jsx file with a polyfill for the Resize Observer API:

import 'resize-observer-polyfill'

function MyApp({ Component, pageProps }) {
  return <Component {...pageProps} />
}

export default MyApp

Popular polyfill libraries include:

  • core-js – one of the most comprehensive polyfills libraries available; it provides polyfills for a wide range of ECMAScript features. Some of the features are already included in Next.js polyfills.
  • regenerator-runtime – a companion library to core-js that provides runtime support for generator functions and async/await.
  • whatwg-fetch – a polyfill for the Fetch API. It is already included in Next.js polyfills
  • promise-polyfill – it provides support for the Promise API.
  • IntersectionObserver Polyfill – it provides support for the Intersection Observer API, which is useful for lazy loading and other performance optimizations.
  • Custom Event Polyfill – it provides support for the CustomEvent API, which is not available in some older browsers.
  • MutationObserver Polyfill – it provides support for the MutationObserver API, which is useful for detecting changes in the DOM.
  • URL Polyfill – it provides support for the URL API, which is not available in some older browsers.

CSS compatibility

To ensure CSS compatibility in your Next.js website or web application, you can enable Autoprefixer, which is a PostCSS plugin that adds vendor prefixes to CSS rules to ensure compatibility with older browsers. PostCSS is a software development tool for transforming CSS with JavaScript-based plugin system. PostCSS is enabled in Next.js projects by default.

To enable Autoprefixer, install it with this command:

npm install autoprefixer –save-dev

After installing Autoprefixer, enable it by modifying the PostCSS configuration file (for example postcss.config.mjs). The modified configuration file looks like this (Tailwind CSS is also installed as a PostCSS plugin):

/** @type {import('postcss-load-config').Config} */
const config = {
  plugins: {
    autoprefixer: {},
    tailwindcss: {},
  },
};

export default config;

After configuring PostCSS to enable Autoprefixer, you can add the supported browser list for PostCSS to use in browserslist property in the package.json file. The package.json file would look like this after adding the browser list:

{
  "name": "example-next-app",
  "version": "0.1.0",
  "private": true,
  "scripts": {
    "dev": "next dev",
    "build": "next build && swc .next/static/chunks/*.js .next/static/chunks/**/*.js -d . --include-dotfiles",
    "start": "next start",
    "lint": "next lint"
  },
  "dependencies": {
    "react": "^18",
    "react-dom": "^18",
    "next": "14.2.5"
  },
  "devDependencies": {
    "typescript": "^5",
    "@swc/cli": "^0.4.1-nightly.20240914",
    "@types/node": "^20",
    "@types/react": "^18",
    "@types/react-dom": "^18",
    "postcss": "^8",
    "tailwindcss": "^3.4.1",
    "eslint": "^8",
    "eslint-config-next": "14.2.5"
  },
  "browserslist": [
    "chrome 64",
    "edge 79",
    "firefox 67",
    "opera 51",
    "safari 12"
  ]
}

You can check and select the browser list at Browserslist.

Testing for browser compatibility

Testing for browser compatibility is a critical step in ensuring that your Next.js application works well across different browsers and their various versions. Here are some strategies and tools you can use to effectively test browser compatibility:

  • Cross-Browser Testing:
    • Test your application on multiple browsers, including modern browsers like Chrome, Firefox, Safari, and Edge, as well as older browsers like Internet Explorer 11.
    • Ensure that your application's layout, functionality, and performance are consistent across all browsers.
  • Responsive Design Testing:
    • Test your application on different screen sizes and devices to ensure that it is responsive and looks good on all devices.
    • Use tools like Chrome DevTools to simulate different screen sizes and devices.
  • Automated Testing:
    • Use automated testing tools to run tests across multiple browsers and devices simultaneously.
    • Write tests for critical functionality and ensure that they pass on all supported browsers.
  • Manual Testing:
    • Perform manual testing to catch issues that automated tests might miss.
    • Test common user flows and edge cases to ensure that your application works as expected.
  • Performance Testing:
    • Test your application's performance on different browsers and devices.
    • Use tools like Lighthouse to measure performance metrics and identify areas for improvement.
  • Accessibility Testing:
    • Ensure that your application is accessible to users with disabilities.
    • Use tools like the WAVE Web Accessibility Evaluation Tool to identify and fix accessibility issues.

Here are some tools to test browser compatibility:

  • BrowserStack:
    • BrowserStack is a popular cloud-based testing platform that allows you to test your application on a wide range of browsers and devices.
    • Features include live interactive testing, automated testing, and visual regression testing.
    • Website: BrowserStack
  • Sauce Labs:
    • Sauce Labs is another cloud-based testing platform that provides access to a large number of browsers and devices for testing.
    • Features include automated testing, live interactive testing, and visual testing.
    • Website: Sauce Labs
  • LambdaTest:
    • LambdaTest is a cloud-based testing platform that offers cross-browser testing, automated testing, and real-time testing.
    • Features include live interactive testing, automated screenshot testing, and responsive testing.
    • Website: LambdaTest

Here are best practices for testing browser compatibility:

  • Define Your Supported Browsers:
    • Clearly define the browsers and versions that you will support.
    • Prioritize testing based on your target audience and analytics data.
  • Use a Testing Matrix:
    • Create a testing matrix that lists all the browsers, versions, and devices you need to test.
    • Track the status of each test case and ensure that all critical functionality is covered.
  • Automate Where Possible:
    • Automate repetitive tests to save time and ensure consistency.
    • Use tools like Selenium, Cypress, or Puppeteer to write automated tests.
  • Test Early and Often:
    • Incorporate browser compatibility testing into your development workflow.
    • Test early in the development process to catch issues before they become more difficult to fix.
  • Document and Report Issues:
    • Document any issues you find during testing and report them to your development team.
    • Use a bug tracking system to track and prioritize issues.
  • Continuous Integration:
    • Integrate browser compatibility testing into your continuous integration (CI) pipeline.
    • Automatically run tests on every commit to catch issues early.

Conclusion

Ensuring browser compatibility for your Next.js application is crucial for providing a seamless and inclusive user experience. By understanding the importance of supporting older browsers, you can reach a wider audience and ensure that your application is accessible to users with various technological constraints.

Throughout this blog post, we've covered the essential aspects of browser compatibility, including transpilation with SWC, using polyfills to bridge the gap between modern and older browsers, and ensuring CSS compatibility with tools like Autoprefixer. We've also explored various strategies and tools for testing browser compatibility, such as BrowserStack, Sauce Labs, and LambdaTest, which can help you identify and fix issues across different browsers and devices.

By following the best practices outlined in this post, you can create a robust and accessible Next.js application that works well across a wide range of browsers. Remember, the key to successful browser compatibility is continuous testing and iteration. Regularly update your testing matrix, automate where possible, and document any issues you encounter to ensure that your application remains compatible and performant.

In conclusion, investing time and effort into browser compatibility not only enhances the user experience but also broadens your application's reach. By supporting older browsers, you demonstrate a commitment to inclusivity and accessibility, which can ultimately lead to greater user satisfaction and loyalty.

Happy coding, and may your Next.js applications be compatible and accessible to all!