diff --git a/package.json b/package.json
index 99549173..58a1733d 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "react-storefront",
- "version": "8.7.3",
+ "version": "8.8.0",
"description": "Build and deploy e-commerce progressive web apps (PWAs) in record time.",
"module": "./index.js",
"license": "Apache-2.0",
diff --git a/src/NextScript.js b/src/NextScript.js
new file mode 100644
index 00000000..041160f2
--- /dev/null
+++ b/src/NextScript.js
@@ -0,0 +1,30 @@
+import React from 'react'
+import { NextScript as OriginalNextScript } from 'next/document'
+import PropTypes from 'prop-types'
+
+/**
+ * A replacement for NextScript from `next/document` that gives you greater control over how script elements are rendered.
+ * This should be used in the body of `pages/_document.js` in place of `NextScript`.
+ */
+export default class NextScript extends OriginalNextScript {
+ static propTypes = {
+ /**
+ * Set to `defer` to use `defer` instead of `async` when rendering script elements.
+ */
+ mode: PropTypes.oneOf(['async', 'defer']),
+ }
+
+ static defaultProps = {
+ mode: 'async',
+ }
+
+ getScripts() {
+ return super.getScripts().map(script => {
+ return React.cloneElement(script, {
+ key: script.props.src,
+ defer: this.props.mode === 'defer' ? true : undefined,
+ async: this.props.mode === 'async' ? true : undefined,
+ })
+ })
+ }
+}
diff --git a/test/NextScript.test.js b/test/NextScript.test.js
new file mode 100644
index 00000000..ca38a4bf
--- /dev/null
+++ b/test/NextScript.test.js
@@ -0,0 +1,38 @@
+import React, { Component } from 'react'
+import { mount } from 'enzyme'
+
+describe('NextScriptDeferred', () => {
+ let NextScript
+
+ beforeEach(() => {
+ jest.isolateModules(() => {
+ jest.doMock('next/document', () => ({
+ NextScript: class OriginalNextScript extends Component {
+ getScripts() {
+ return []
+ }
+
+ render() {
+ return <>{this.getScripts()}>
+ }
+ },
+ }))
+
+ NextScript = require('react-storefront/NextScript').default
+ })
+ })
+
+ it('should remove async and add defer to all script tags returned from super.getScripts', () => {
+ const wrapper = mount()
+ const scriptProps = wrapper.find('script').props()
+ expect(scriptProps.async).toBe(undefined)
+ expect(scriptProps.defer).toBe(true)
+ })
+
+ it('should use async by default', () => {
+ const wrapper = mount()
+ const scriptProps = wrapper.find('script').props()
+ expect(scriptProps.async).toBe(true)
+ expect(scriptProps.defer).toBe(undefined)
+ })
+})