React index layout: collection override:tags: [] date: 2018-11-05 pathName: react Accessibility auditing with react-axe and eslint-plugin-jsx-a11y If you would like to learn about the basic concepts behind accessibility in web pages, refer to the What is accessibility guide first. react-axe is a library that audits a React application and logs any accessibility issues to the Chrome DevTools console. It uses the open-source axe testing library to flag any issues and their severity. eslint-plugin-jsx-a11y is an ESLint plugin that identifies and enforces a number of accessibility rules directly in your JSX. Using this in combination with a tool that tests the final rendered DOM, such as react-axe, can help you find and fix any accessibility concerns on your site. Why is this useful? It is crucial to build websites that provide every user, regardless of their impairment or restriction, the capability to access its content. Using auditing libraries such as react-axe and eslint-plugin-jsx-a11y during the development of your React application will automatically expose any accessibility issues as they pop up. Use eslint-plugin-jsx-a11y React already supports writing accessible HTML elements within JSX syntax. For example, you only need to use the htmlFor attribute instead of for to link a label to a specific form element within a React component. The React accessibility documentation covers all the nuances of handling accessibility concerns within a React component. To make it easier to spot these issues during development, Create React App (CRA) includes the eslint-plugin-jsx-a11y plugin for ESLint by default. To enable pre-configured linting provided by CRA: Install the appropriate ESLint plugin for your code editor Add a .eslintrc.json file to your project { "extends": "react-app" } You can find more details about configuring your editor to support out-of-box linting in the CRA documentation. Some common accessibility issues will now show up. Image accessibility warning in linter To check for even more accessibility rules, modify the file to automatically include all the recommended rules by the plugin: { "extends": ["react-app", "plugin:jsx-a11y/recommended"] } If you would like an even stricter subset of rules, switch to strict mode: { "extends": ["react-app", "plugin:jsx-a11y/strict"] } Label accessibility error in linter The project documentation provides information on the differences between recommended and strict mode. Use react-axe eslint-plugin-jsx-a11y can help you easily pinpoint any accessibility issues in your JSX, but it does not test any of the final HTML output. react-axe is a library that does exactly this by providing a React wrapper around the axe-core testing tool by Deque Labs. Install the library as a development dependency to begin: npm install --save-dev react-axe You now only need to initialize the module in index.js: if (process.env.NODE_ENV !== 'production') { import('react-axe').then(axe => { axe.default(React, ReactDOM, 1000); ReactDOM.render(, document.getElementById('root')); }); } else { ReactDOM.render(, document.getElementById('root')); } A dynamic import is used here to only load the library when it is not in production mode before rendering and booting up the root App component. This ensures that it is not unnecessarily included in the final production bundle. Now when you run the application during development, issues are surfaced directly to the Chrome DevTools console. React Axe in Chrome DevTools A severity level is also assigned for each violation. These levels are: Minor Moderate Serious Critical If you would like to include accessibility testing in your unit testing workflow, take a look at the Jest and axe integration example to understand how. Conclusion If you are building a site with React, include accessibility auditing into your workflow early to catch problems as you build your components. Use eslint-plugin-jsx-a11y to add accessibility checks into your linting workflow. CRA already comes with it included, but switch to either the recommended or strict mode. In addition to local development testing, include react-axe into your application to catch any issues on the final rendered DOM. Do not include it into your production bundle. Add a web app manifest with Create React App If you don't know how web app manifest files work, refer to the Add a web app manifest guide first. Create React App (CRA) includes a web app manifest by default. Modifying this file will allow you to change how your application is displayed when installed on the user's device. A progressive web app icon on a mobile phone's home screen Why is this useful? Web app manifest files provide the capability to change how an installed application will look like on the user's desktop or mobile device. By modifying properties in the JSON file, you can modify a number of details in your application, including its: Name Description App icon Theme color The MDN documentation covers all the properties that can be changed in detail. Modify the default manifest In CRA, a default manifest file, /public/manifest.json is included automatically when a new app is created: { "short_name": "React App", "name": "Create React App Sample", "icons": [ { "src": "favicon.ico", "sizes": "64x64 32x32 24x24 16x16", "type": "image/x-icon" }, { "src": "logo192.png", "type": "image/png", "sizes": "192x192" }, { "src": "logo512.png", "type": "image/png", "sizes": "512x512" } ], "start_url": ".", "display": "standalone", "theme_color": "#000000", "background_color": "#ffffff" } This allows anybody to install the application on their device and see some default details of the application. The HTML file, public/index.html, also includes a element to load the manifest. Here is an example of an application built with CRA that has a modified manifest file: https://glitch.com/edit/#!/cra-web-app-manifest-defaut To find out if all the properties are working correctly in this example: In the Application panel, click the Manifest tab. DevTool's Manifest tab shows the properties from the app manifest file. Conclusion If you're building a site that you think does not need to be installed on a device, remove the manifest and the element in the HTML file that points to it. If you would like users to install the application on their device, modify the manifest file (or create one if you are not using CRA) with any properties that you like. The MDN documentation explains all the required and optional attributes. Code splitting with React.lazy and Suspense If you don't yet understand the basic idea behind code splitting, refer to Reduce JavaScript payloads with code splitting guide first. The React.lazy method makes it easy to code-split a React application on a component level using dynamic imports. import React, { lazy } from 'react'; const AvatarComponent = lazy(() => import('./AvatarComponent')); const DetailsComponent = () => (
) Why is this useful? A large React application will usually consist of many components, utility methods, and third-party libraries. If an effort isn't made to try to load different parts of an application only when they're needed, a single, large bundle of JavaScript will be shipped to your users as soon as they load the first page. This can affect page performance significantly. The React.lazy function provides a built-in way to separate components in an application into separate chunks of JavaScript with very little legwork. You can then take care of loading states when you couple it with the Suspense component. Suspense The problem with shipping a large JavaScript payload to users is the length of time it would take for the page to finish loading, especially on weaker devices and network connections. This is why code splitting and lazy loading is extremely useful. However, there will always be a slight delay that users have to experience when a code-split component is being fetched over the network, so it's important to display a useful loading state. Using React.lazy with the Suspense component helps solve this problem. import React, { lazy, Suspense } from 'react'; const AvatarComponent = lazy(() => import('./AvatarComponent')); const renderLoader = () =>

Loading

; const DetailsComponent = () => ( ) Suspense accepts a fallback component which allows you to display any React component as a loading state. The following example shows how this works. The avatar is only rendered when the button is clicked, where a request is then made to retrieve the code necessary for the suspended AvatarComponent. In the meantime, the fallback loading component is shown. https://glitch.com/edit/#!/react-lazy-suspense In here, the code that makes up AvatarComponent is small which is why the loading spinner only shows for a short amount of time. Larger components can take much longer to load, especially on weak network connections. To better demonstrate how this works: Click the Throttling dropdown, which is set to No throttling by default. Select Fast 3G. Click the Click Me button in the app. The loading indicator will show for longer now. Notice how all the code that makes up the AvatarComponent is fetched as a separate chunk. DevTools network panel showing one chunk.js file being downloaded React does not currently support Suspense when components are being server-side rendered. If you are rendering on the server, consider using another library such as loadable-components which is recommended in the React docs. Suspending multiple components Another feature of Suspense is that it allows you to suspend multiple components from loading, even if they are all lazy loaded. For example: import React, { lazy, Suspense } from 'react'; const AvatarComponent = lazy(() => import('./AvatarComponent')); const InfoComponent = lazy(() => import('./InfoComponent')); const MoreInfoComponent = lazy(() => import('./MoreInfoComponent')); const renderLoader = () =>

Loading

; const DetailsComponent = () => ( ) This is an extremely useful way to delay rendering of multiple components while only showing a single loading state. Once all the components have finished fetching, the user gets to see them all displayed at the same time. You can see this with the following embed: https://glitch.com/edit/#!/react-lazy-suspense-multiple Loading indicator showing a little too quickly? Try simulating a throttled connection in DevTools again. Without this, it's easy to run into the problem of staggered loading, or different parts of a UI loading one after the other with each having their own loading indicator. This can make the user experience feel more jarring. Although using Suspense to split components is already possible and makes it easy to trim down bundle sizes, the React team is continuing to work on more features that would extend this even further. The React 16.x roadmap explains this in more detail. Handle loading failures Suspense allows you to display a temporary loading state while network requests are made under the hood. But what if those network requests fail for some reason? You might be offline, or perhaps your web app is attempting to lazy-load a versioned URL that is out of date, and no longer available following a server redeployment. React has a standard pattern for gracefully handling these types of loading failures: using an error boundary. As described in the documentation, any React component can serve as an error boundary if it implements either (or both) of the lifecycle methods static getDerivedStateFromError() or componentDidCatch(). To detect and handle lazy loading failures, you can wrap your Suspense component with a parent components that serves as an error boundary. Inside the error boundary's render() method, you can render the children as-is if there's no error, or render a custom error message if something goes wrong: import React, { lazy, Suspense } from 'react'; const AvatarComponent = lazy(() => import('./AvatarComponent')); const InfoComponent = lazy(() => import('./InfoComponent')); const MoreInfoComponent = lazy(() => import('./MoreInfoComponent')); const renderLoader = () =>

Loading

; class ErrorBoundary extends React.Component { constructor(props) { super(props); this.state = {hasError: false}; } static getDerivedStateFromError(error) { return {hasError: true}; } render() { if (this.state.hasError) { return

Loading failed! Please reload.

; } return this.props.children; } } const DetailsComponent = () => ( ) Conclusion If you are unsure where to begin applying code splitting to your React application, follow these steps: Begin at the route level. Routes are the simplest way to identify points of your application that can be split. The React docs show how Suspense can be used along with react-router. Identify any large components on a page on your site that only render on certain user interactions (like clicking a button). Splitting these components will minimize your JavaScript payloads. Consider splitting anything else that is offscreen and not critical for the user. Code splitting with dynamic imports in Next.js This post explains different types of code splitting and how to use dynamic imports to speed up your Next.js apps. Route-based and component-based code splitting By default, Next.js splits your JavaScript into separate chunks for each route. When users load your application, Next.js only sends the code needed for the initial route. When users navigate around the application, they fetch the chunks associated with the other routes. Route-based code splitting minimizes the amount of script that needs to be parsed and compiled at once, which results in faster page load times. While route-based code splitting is a good default, you can further optimize the loading process with code splitting on the component level. If you have large components in your app, it's a great idea to split them into separate chunks. That way, any large components that are not critical or only render on certain user interactions (like clicking a button) can be lazy-loaded. Next.js supports dynamic import(), which allows you to import JavaScript modules (including React components) dynamically and load each import as a separate chunk. This gives you component-level code splitting and enables you to control resource loading so that users only download the code they need for the part of the site that they're viewing. In Next.js, these components are server-side rendered (SSR) by default. Dynamic imports in action This post includes several versions of a sample app that consists of a simple page with one button. When you click the button, you get to see a cute puppy. As you move through each version of the app, you'll see how dynamic imports are different from static imports and how to work with them. In the first version of the app, the puppy lives in components/Puppy.js. To display the puppy on the page, the app imports the Puppy component in index.js with a static import statement: import Puppy from "../components/Puppy"; https://glitch.com/edit/#!/static-import To see how Next.js bundles the app, inspect the network trace in DevTools: When you load the page, all the necessary code, including the Puppy.js component, is bundled in index.js: DevTools Network tab showing showing six JavaScript files: index.js, app.js, webpack.js, main.js, 0.js and the dll (dynamic-link library) file. When you press the Click me button, only the request for the puppy JPEG is added to the Network tab: DevTools Network tab after the button click, showing the same six JavaScript files and one image. The downside of this approach is that even if users don't click the button to see the puppy, they have to load the Puppy component because it's included in index.js. In this little example that's not a big deal, but in real-world applications it's often a huge improvement to load large components only when necessary. Now check out a second version of the app, in which the static import is replaced with a dynamic import. Next.js includes next/dynamic, which makes it possible to use dynamic imports for any components in Next: import Puppy from "../components/Puppy"; import dynamic from "next/dynamic"; // ... const Puppy = dynamic(import("../components/Puppy")); https://glitch.com/edit/#!/dynamic-import-nextjs Follow the steps from the first example to inspect the network trace. When you first load the app, only index.js is downloaded. This time it's 0.5 KB smaller (it went down from 37.9 KB to 37.4 KB) because it doesn't include the code for the Puppy component: DevTools Network showing the same six JavaScript files, except index.js is now 0.5 KB smaller. The Puppy component is now in a separate chunk, 1.js, which is loaded only when you press the button: DevTools Network tab after the button click, showing the additional 1.js file and the image added to the bottom of the file list. By default, Next.js names these dynamic chunks number.js, where number starts from 1. In real-world applications, components are often much larger, and lazy loading them can trim your initial JavaScript payload by hundreds of kilobytes. Dynamic imports with custom loading indicator When you lazy-load resources, it's good practice to provide a loading indicator in case there are any delays. In Next.js, you can do that by providing an additional argument to the dynamic() function: const Puppy = dynamic(() => import("../components/Puppy"), { loading: () =>

Loading...

}); https://glitch.com/edit/#!/dynamic-import-loading To see the loading indictor in action, simulate slow network connection in DevTools: In the Throttling drop-down list, select Fast 3G. Press the Click me button. Now when you click the button it takes a while to load the component and the app displays the "Loading…" message in the meantime. *A dark screen with the text * Dynamic imports without SSR If you need to render a component only on the client side (for example, a chat widget) you can do that by setting the ssr option to false: const Puppy = dynamic(() => import("../components/Puppy"), { ssr: false, }); https://glitch.com/edit/#!/dynamic-import-no-ssr Conclusion With support for dynamic imports, Next.js gives you component-level code splitting, which can minimize your JavaScript payloads and improve application load time. All components are server-side rendered by default and you can disable this option whenever necessary. Prefetching in create-react-app with Quicklink This codelab uses Chrome DevTools. Download Chrome if you don't already have it. This codelab shows you how to implement the Quicklink library in a React SPA demo to demonstrate how prefetching speeds up subsequent navigations. Measure Before adding optimizations, it's always a good idea to first analyze the current state of the application. The website is a simple demo built with create-react-app. Complete the following instructions in the new tab that just opened: In the Throttling drop-down list, select Fast 3G to simulate a slow connection type. Type chunk into the Filter textbox to hide any resources that do not include chunk in their name. Network panel showing the home page chunks. The site uses route-based code splitting, thanks to which only the necessary code is requested at the beginning. Clear the network requests in DevTools. Within the app, click the Blog link to navigate to that page. The JS and CSS chunks for the new route are loaded to render the page. Network panel showing the blog page chunks. Next, you'll implement Quicklink in this site, so that these chunks can be prefetched in the home page, making the navigation faster. This allows you to combine the best of both techniques: Route-based code splitting tells the browser to only load the necessary chunks at a higher priority at page load time. Prefetching tells the browser to load the chunks for in-viewport links at the lowest priority, during the browser's idle time. Configure webpack-route-manifest The first step is to install and configure webpack-route-manifest, a webpack plugin that lets you generate a manifest file associating routes with their corresponding chunks. Usually, you would need to install the library, but we've already done it for you. Here's the command that you would need to run: npm install webpack-route-manifest --save-dev config-overrides.js is a file placed in your project root directory where you can override existing behaviour of the webpack configuration, without having to eject the project. Open config-overrides.js for edit and add the webpack-route-manifest dependency at the beginning of the file: const path = require('path'); const RouteManifest = require('webpack-route-manifest'); Next, configure the webpack-route-manifest plugin by adding the following code to the bottom of config-overrides.js: module.exports = function override(config) { config.resolve = { ...config.resolve, alias: { '@assets': `${path.resolve(__dirname, 'src/assets')}`, '@pages': `${path.resolve(__dirname, 'src/pages')}`, '@components': `${path.resolve(__dirname, 'src/components')}`, }, }; config.plugins.push( new RouteManifest({ minify: true, filename: 'rmanifest.json', routes(str) { let out = str.replace('@pages', '').toLowerCase(); if (out === '/article') return '/blog/:title'; if (out === '/home') return '/'; return out; }, }), ); return config; }; The new code does the following: config.resolve declares variables with the internal routes to pages, assets and components. config.plugins.push() creates a RouteManifest object and passes it the configuration so that the rmanifest.json file can be generated based on the site's routes and chunks. The manifest.json file will be generated and made available at https://site_url/rmanifest.json. Configure quicklink At this point you would need to install the Quicklink library in your project. For simplicity, we already added it to the project. Here's the command that you would need to run: npm install --save quicklink Open src/components/App/index.js for edit. First, import the Quicklink higher order component (HOC): import React, { lazy, Suspense } from 'react'; import { Route } from 'react-router-dom'; import Footer from '@components/Footer'; import Hero from '@components/Hero'; import style from './index.module.css'; import { withQuicklink } from 'quicklink/dist/react/hoc.js'; const Home = lazy(() => import(/* webpackChunkName: "home" */ '@pages/Home')); const About = lazy(() => import(/* webpackChunkName: "about" */ '@pages/About')); const Article = lazy(() => import(/* webpackChunkName: "article" */ '@pages/Article')); const Blog = lazy(() => import(/* webpackChunkName: "blog" */ '@pages/Blog')); Next, create an options object after the Blog variable declaration, to use as an argument when calling quicklink: const options = { origins: [] }; Finally, wrap each route with the withQuicklink() higher order component, passing it an options parameter and the target component for that route: const App = () => (
Loading...
}>