/
snippet.tsx
65 lines (58 loc) 路 2.43 KB
/
snippet.tsx
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
import React from 'react';
import type { PartytownConfig } from '../lib/types';
import { partytownSnippet } from '@builder.io/partytown/integration';
/**
* Props for `<Partytown/>`, which extends the Partytown Config.
*
* https://github.com/BuilderIO/partytown#config
*
* @public
*/
export interface PartytownProps extends PartytownConfig {}
/**
* The React `<Partytown/>` component should be placed within the `<head>`
* of the document. This component should work for SSR/SSG only HTML
* (static HTML without javascript), clientside javascript only
* (entire React app is build with clientside javascript),
* and both SSR/SSG HTML that's then hydrated by the client.
*
* @public
*/
export const Partytown = ({ nonce, ...props }: PartytownProps = {}): any => {
// purposely not using useState() or useEffect() so this component
// can also work as a React Server Component
// this check is only be done on the client, and skipped over on the server
if (typeof document !== 'undefined' && !document._partytown) {
if (!document.querySelector('script[data-partytown]')) {
// the append script to document code should only run on the client
// and only if the SSR'd script doesn't already exist within the document.
// If the SSR'd script isn't found in the document, then this
// must be a clientside only render. Append the partytown script
// to the <head>.
const scriptElm = document.createElement('script');
scriptElm.dataset.partytown = '';
scriptElm.innerHTML = partytownSnippet(props);
scriptElm.nonce = nonce;
document.head.appendChild(scriptElm);
}
// should only append this script once per document, and is not dynamic
document._partytown = true;
}
// `dangerouslySetInnerHTML` only works for scripts rendered as HTML from SSR.
// The added code will set the [type="data-pt-script"] attribute to the SSR rendered
// <script>. If this code renders as SSR HTML, then on the client it'll execute
// and add the attribute which will tell the Client JS of the component to NOT
// add the same script to the <head>.
const innerHTML = partytownSnippet(props) + 'document.currentScript.dataset.partytown="";';
return (
<script
suppressHydrationWarning
dangerouslySetInnerHTML={{ __html: innerHTML }}
nonce={nonce}
/>
);
};
interface PartytownDocument extends Document {
_partytown?: boolean;
}
declare const document: PartytownDocument;