Script examples (#31181)
* Add script component examples * Update script examples * Refactor code * Remove unused files * Fix linter * Fix lint error * Fix prettier
This commit is contained in:
parent
b79591cdaf
commit
d550b13b8d
13 changed files with 338 additions and 0 deletions
|
@ -7,6 +7,7 @@ description: Next.js helps you optimize loading third-party scripts with the bui
|
|||
<details>
|
||||
<summary><b>Examples</b></summary>
|
||||
<ul>
|
||||
<li><a href="https://github.com/vercel/next.js/tree/canary/examples/script-component">Script Component</a></li>
|
||||
<li><a href="https://github.com/vercel/next.js/tree/canary/examples/with-google-tag-manager">Google Tag Manager</a></li>
|
||||
<li><a href="https://github.com/vercel/next.js/tree/canary/examples/with-google-analytics">Google Analytics</a></li>
|
||||
<li><a href="https://github.com/vercel/next.js/tree/canary/examples/with-facebook-pixel">Facebook Pixel</a></li>
|
||||
|
|
34
examples/script-component/.gitignore
vendored
Normal file
34
examples/script-component/.gitignore
vendored
Normal file
|
@ -0,0 +1,34 @@
|
|||
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
|
||||
|
||||
# dependencies
|
||||
/node_modules
|
||||
/.pnp
|
||||
.pnp.js
|
||||
|
||||
# testing
|
||||
/coverage
|
||||
|
||||
# next.js
|
||||
/.next/
|
||||
/out/
|
||||
|
||||
# production
|
||||
/build
|
||||
|
||||
# misc
|
||||
.DS_Store
|
||||
*.pem
|
||||
|
||||
# debug
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
|
||||
# local env files
|
||||
.env.local
|
||||
.env.development.local
|
||||
.env.test.local
|
||||
.env.production.local
|
||||
|
||||
# vercel
|
||||
.vercel
|
33
examples/script-component/README.md
Normal file
33
examples/script-component/README.md
Normal file
|
@ -0,0 +1,33 @@
|
|||
# Script component
|
||||
|
||||
This example shows different strategies that can be used for the [`next/script` component](https://nextjs.org/docs/basic-features/script):
|
||||
|
||||
- [Loading Polyfills](./pages/polyfill.js)
|
||||
- [Lazy loading](./pages/lazy.js)
|
||||
- [Executing code after loading](./pages/onload.js)
|
||||
- [Inline scripts](./pages/inline.js)
|
||||
- [Forwarding attributes](./pages/attributes.js)
|
||||
|
||||
## Preview
|
||||
|
||||
Preview the example live on [StackBlitz](http://stackblitz.com/):
|
||||
|
||||
[![Open in StackBlitz](https://developer.stackblitz.com/img/open_in_stackblitz.svg)](https://stackblitz.com/github/vercel/next.js/tree/canary/examples/script-component)
|
||||
|
||||
## Deploy your own
|
||||
|
||||
Deploy the example using [Vercel](https://vercel.com?utm_source=github&utm_medium=readme&utm_campaign=next-example):
|
||||
|
||||
[![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/new/git/external?repository-url=https://github.com/vercel/next.js/tree/canary/examples/script-component&project-name=script-component&repository-name=script-component)
|
||||
|
||||
## How to use
|
||||
|
||||
Execute [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app) with [npm](https://docs.npmjs.com/cli/init) or [Yarn](https://yarnpkg.com/lang/en/docs/cli/create/) to bootstrap the example:
|
||||
|
||||
```bash
|
||||
npx create-next-app --example script-component script-component-app
|
||||
# or
|
||||
yarn create next-app --example script-component script-component-app
|
||||
```
|
||||
|
||||
Deploy it to the cloud with [Vercel](https://vercel.com/new?utm_source=github&utm_medium=readme&utm_campaign=next-example) ([Documentation](https://nextjs.org/docs/deployment)).
|
13
examples/script-component/package.json
Normal file
13
examples/script-component/package.json
Normal file
|
@ -0,0 +1,13 @@
|
|||
{
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "next",
|
||||
"build": "next build",
|
||||
"start": "next start"
|
||||
},
|
||||
"dependencies": {
|
||||
"next": "latest",
|
||||
"react": "^17.0.2",
|
||||
"react-dom": "^17.0.2"
|
||||
}
|
||||
}
|
20
examples/script-component/pages/_app.js
Normal file
20
examples/script-component/pages/_app.js
Normal file
|
@ -0,0 +1,20 @@
|
|||
import Link from 'next/link'
|
||||
|
||||
import '../styles/global.css'
|
||||
|
||||
const MyApp = ({ Component, pageProps, router }) => {
|
||||
const pathname = router.pathname
|
||||
|
||||
return (
|
||||
<>
|
||||
<Component {...pageProps} />
|
||||
{pathname !== '/' && (
|
||||
<Link href="/">
|
||||
<a>See all examples</a>
|
||||
</Link>
|
||||
)}
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default MyApp
|
22
examples/script-component/pages/attributes.js
Normal file
22
examples/script-component/pages/attributes.js
Normal file
|
@ -0,0 +1,22 @@
|
|||
import Script from 'next/script'
|
||||
|
||||
export default function Inline() {
|
||||
return (
|
||||
<>
|
||||
{/* Attributes are forwarded */}
|
||||
<Script
|
||||
src="https://www.google-analytics.com/analytics.js"
|
||||
id="analytics"
|
||||
nonce="XUENAJFW"
|
||||
data-test="analytics"
|
||||
/>
|
||||
|
||||
<main>
|
||||
<h1>Forwarded attributes</h1>
|
||||
<h5>
|
||||
Open devtools and check that attributes has been forwarded correctly.
|
||||
</h5>
|
||||
</main>
|
||||
</>
|
||||
)
|
||||
}
|
36
examples/script-component/pages/index.js
Normal file
36
examples/script-component/pages/index.js
Normal file
|
@ -0,0 +1,36 @@
|
|||
import Link from 'next/link'
|
||||
|
||||
export default function Index() {
|
||||
return (
|
||||
<main>
|
||||
<h1>Script component examples</h1>
|
||||
<ul>
|
||||
<li>
|
||||
<Link href="/polyfill">
|
||||
<a>Polyfill</a>
|
||||
</Link>
|
||||
</li>
|
||||
<li>
|
||||
<Link href="/lazy">
|
||||
<a>Lazy Loading</a>
|
||||
</Link>
|
||||
</li>
|
||||
<li>
|
||||
<Link href="/onload">
|
||||
<a>Executing code after loading</a>
|
||||
</Link>
|
||||
</li>
|
||||
<li>
|
||||
<Link href="/inline">
|
||||
<a>Inline scripts</a>
|
||||
</Link>
|
||||
</li>
|
||||
<li>
|
||||
<Link href="/attributes">
|
||||
<a>Forwarding attributes</a>
|
||||
</Link>
|
||||
</li>
|
||||
</ul>
|
||||
</main>
|
||||
)
|
||||
}
|
29
examples/script-component/pages/inline.js
Normal file
29
examples/script-component/pages/inline.js
Normal file
|
@ -0,0 +1,29 @@
|
|||
import Script from 'next/script'
|
||||
|
||||
export default function Inline() {
|
||||
return (
|
||||
<>
|
||||
{/* Execute arbitrary code */}
|
||||
<Script id="show-banner" strategy="lazyOnload">
|
||||
{`document.getElementById('banner').classList.remove('hidden')`}
|
||||
</Script>
|
||||
|
||||
{/* Or */}
|
||||
|
||||
{/* <Script
|
||||
id="show-banner"
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: `document.getElementById('banner').classList.remove('hidden')`
|
||||
}}
|
||||
/> */}
|
||||
|
||||
<main>
|
||||
<h1>Inline scripts</h1>
|
||||
<h5 id="banner" className="hidden">
|
||||
This is initially hidden but its being shown because the `Script`
|
||||
component removed the `hidden` class.
|
||||
</h5>
|
||||
</main>
|
||||
</>
|
||||
)
|
||||
}
|
42
examples/script-component/pages/lazy.js
Normal file
42
examples/script-component/pages/lazy.js
Normal file
|
@ -0,0 +1,42 @@
|
|||
import { useCallback, useEffect, useState } from 'react'
|
||||
import Script from 'next/script'
|
||||
|
||||
export default function Lazyload() {
|
||||
const [log, setLog] = useState([])
|
||||
|
||||
const addLog = useCallback(
|
||||
(text) => {
|
||||
setLog((log) => log.concat({ time: new Date(), text }))
|
||||
},
|
||||
[setLog]
|
||||
)
|
||||
|
||||
useEffect(() => {
|
||||
addLog(`Page loaded window.FB is undefined`)
|
||||
}, [addLog])
|
||||
|
||||
return (
|
||||
<>
|
||||
{/* We lazy load the FB SDK */}
|
||||
<Script
|
||||
src="https://connect.facebook.net/en_US/sdk.js"
|
||||
strategy="lazyOnload"
|
||||
onLoad={() =>
|
||||
addLog(`script loaded correctly, window.FB has been populated`)
|
||||
}
|
||||
/>
|
||||
|
||||
<main>
|
||||
<h1>Lazy Loading FB sdk</h1>
|
||||
<h5>You can check `window.FB` on browser console</h5>
|
||||
<ul>
|
||||
{log.map(({ time, text }) => (
|
||||
<li key={+time}>
|
||||
{time.toISOString()}: {text}
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</main>
|
||||
</>
|
||||
)
|
||||
}
|
46
examples/script-component/pages/onload.js
Normal file
46
examples/script-component/pages/onload.js
Normal file
|
@ -0,0 +1,46 @@
|
|||
import { useMemo, useState } from 'react'
|
||||
import Script from 'next/script'
|
||||
|
||||
export default function Onload() {
|
||||
const [stripe, setStripe] = useState(null)
|
||||
const methods = useMemo(
|
||||
() =>
|
||||
stripe
|
||||
? Object.entries(stripe.stripe).filter(
|
||||
([_key, value]) => typeof value === 'function'
|
||||
)
|
||||
: [],
|
||||
[stripe]
|
||||
)
|
||||
|
||||
function handleLoad() {
|
||||
const stripe = window.Stripe('pk_test_1234')
|
||||
|
||||
console.log('Stripe loaded: ', stripe)
|
||||
|
||||
setStripe({ stripe })
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
{/* We load Stripe sdk */}
|
||||
<Script
|
||||
id="stripe-js"
|
||||
src="https://js.stripe.com/v3/"
|
||||
onLoad={handleLoad}
|
||||
/>
|
||||
|
||||
<main>
|
||||
<h1>Executing code after loading</h1>
|
||||
<div>
|
||||
<p>Stripe methods: </p>
|
||||
<ul>
|
||||
{methods.map(([method]) => (
|
||||
<li key={method}>{method}</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
</main>
|
||||
</>
|
||||
)
|
||||
}
|
48
examples/script-component/pages/polyfill.js
Normal file
48
examples/script-component/pages/polyfill.js
Normal file
|
@ -0,0 +1,48 @@
|
|||
import { useEffect, useRef, useState } from 'react'
|
||||
import Script from 'next/script'
|
||||
|
||||
import s from '../styles/polyfill.module.css'
|
||||
|
||||
export default function Polyfill() {
|
||||
const ref = useRef()
|
||||
const [lastIntersection, setIntersection] = useState(new Date())
|
||||
|
||||
useEffect(() => {
|
||||
const observer = new IntersectionObserver(
|
||||
(intersections) => {
|
||||
const isIntersecting = intersections[0]?.isIntersecting
|
||||
|
||||
if (isIntersecting) {
|
||||
setIntersection(new Date())
|
||||
}
|
||||
},
|
||||
{
|
||||
rootMargin: '45px',
|
||||
}
|
||||
)
|
||||
|
||||
observer.observe(ref.current)
|
||||
|
||||
return () => observer.disconnect()
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<>
|
||||
{/* We ensure that intersection observer is available by polyfilling it */}
|
||||
<Script
|
||||
src="https://polyfill.io/v3/polyfill.min.js?features=IntersectionObserverEntry%2CIntersectionObserver"
|
||||
strategy="beforeInteractive"
|
||||
/>
|
||||
|
||||
<main className={s.container}>
|
||||
<h1>IntersectionObserver Polyfill</h1>
|
||||
<h5>Scroll down to see when was the last intersection</h5>
|
||||
<section className={s.section}>
|
||||
<span ref={ref}>
|
||||
Last intersection at {lastIntersection.toTimeString()}
|
||||
</span>
|
||||
</section>
|
||||
</main>
|
||||
</>
|
||||
)
|
||||
}
|
8
examples/script-component/styles/global.css
Normal file
8
examples/script-component/styles/global.css
Normal file
|
@ -0,0 +1,8 @@
|
|||
* {
|
||||
font-family: sans-serif;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.hidden {
|
||||
display: none;
|
||||
}
|
6
examples/script-component/styles/polyfill.module.css
Normal file
6
examples/script-component/styles/polyfill.module.css
Normal file
|
@ -0,0 +1,6 @@
|
|||
.section {
|
||||
height: 400vh;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
Loading…
Reference in a new issue