Skip to content

Commit 466ac82

Browse files
author
Nathan Booker
committed
improvements
1 parent 2c3fbe2 commit 466ac82

4 files changed

Lines changed: 59 additions & 81 deletions

File tree

core/app/[locale]/layout.tsx

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -115,8 +115,7 @@ export default async function RootLayout({ params, children }: Props) {
115115
<html className={clsx(fonts.map((f) => f.variable))} lang={locale}>
116116
<head>
117117
<ScriptManagerScripts
118-
location="head"
119-
scripts={data.site.content.scripts}
118+
scripts={data.site.content.headerScripts}
120119
strategy="afterInteractive"
121120
/>
122121
</head>
@@ -136,8 +135,7 @@ export default async function RootLayout({ params, children }: Props) {
136135
<VercelComponents />
137136
<ContainerQueryPolyfill />
138137
<ScriptManagerScripts
139-
location="footer"
140-
scripts={data.site.content.scripts}
138+
scripts={data.site.content.footerScripts}
141139
strategy="lazyOnload"
142140
/>
143141
</body>

core/components/scripts/README.md

Lines changed: 24 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -4,36 +4,39 @@ This directory contains components for rendering BigCommerce scripts using Next.
44

55
## Components
66

7-
Both `HeaderScripts` and `FooterScripts` are exported from a single `scripts.tsx` file that follows DRY principles by sharing common logic through an internal `ScriptRenderer` component.
8-
9-
### HeaderScripts
10-
Renders scripts that should be placed in the `<head>` tag with `strategy="afterInteractive"`.
11-
12-
### FooterScripts
13-
Renders scripts that should be placed before the closing `</body>` tag with `strategy="lazyOnload"` for better performance.
7+
### ScriptManagerScripts
8+
A single component that renders scripts with configurable strategy. Location filtering is now handled at the GraphQL level, simplifying the component logic.
149

1510
## Usage
1611

1712
```tsx
18-
import { HeaderScripts, FooterScripts } from '~/components/scripts';
19-
20-
// In your layout or page component:
21-
<HeaderScripts scripts={data.site.content.scripts} />
22-
<FooterScripts scripts={data.site.content.scripts} />
13+
import { ScriptManagerScripts } from '~/components/scripts';
14+
15+
// In your layout component:
16+
<head>
17+
<ScriptManagerScripts
18+
scripts={data.site.content.headerScripts}
19+
strategy="afterInteractive"
20+
/>
21+
</head>
22+
<body>
23+
{children}
24+
<ScriptManagerScripts
25+
scripts={data.site.content.footerScripts}
26+
strategy="lazyOnload"
27+
/>
28+
</body>
2329
```
2430

2531
## Architecture
2632

27-
The components use a shared `ScriptRenderer` component that accepts:
28-
- `scripts`: The GraphQL scripts data
29-
- `location`: 'head' or 'footer'
30-
- `strategy`: 'afterInteractive' or 'lazyOnload'
31-
32-
This ensures consistent behavior and reduces code duplication between header and footer scripts.
33+
The component accepts:
34+
- `scripts`: Pre-filtered GraphQL scripts data (headerScripts or footerScripts)
35+
- `strategy`: 'afterInteractive', 'lazyOnload', or other Next.js Script strategies
3336

3437
## Script Processing
3538

36-
The components handle both types of BigCommerce scripts:
39+
The component handles both types of BigCommerce scripts:
3740

3841
### External Scripts (SrcScript)
3942
```tsx
@@ -42,6 +45,7 @@ The components handle both types of BigCommerce scripts:
4245
src="https://example.com/script.js"
4346
strategy="afterInteractive"
4447
integrity="sha256-abc123..."
48+
crossOrigin="anonymous"
4549
/>
4650
```
4751

@@ -55,37 +59,8 @@ Inline scripts are rendered using `dangerouslySetInnerHTML` for reliable executi
5559
/>
5660
```
5761

58-
## Filtering Logic
59-
60-
Scripts are filtered by:
61-
- **Location**: BigCommerce API returns `'HEAD'` or `'FOOTER'` (uppercase)
62-
- **Visibility**: Only renders scripts with `'ALL_PAGES'` or `'STOREFRONT'` visibility
63-
64-
## Performance
62+
## Performance & Security
6563

6664
- Header scripts use `afterInteractive` strategy for critical functionality
6765
- Footer scripts use `lazyOnload` strategy for better performance
68-
- Script IDs are generated using the `entityId` from the BigCommerce API
6966
- Integrity hashes are included when available for security
70-
- Scripts are efficiently filtered and rendered based on location and visibility
71-
72-
## GraphQL Integration
73-
74-
Uses the `ScriptsFragment` to fetch script data:
75-
```graphql
76-
fragment ScriptsFragment on Content {
77-
scripts(first: 50, filters: { visibilities: [ALL_PAGES, STOREFRONT] }) {
78-
edges {
79-
node {
80-
integrityHashes { hash }
81-
entityId
82-
consentCategory
83-
location
84-
visibility
85-
... on InlineScript { scriptTag }
86-
... on SrcScript { src }
87-
}
88-
}
89-
}
90-
}
91-
```

core/components/scripts/fragment.ts

Lines changed: 30 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -2,23 +2,36 @@ import { graphql } from '~/client/graphql';
22

33
export const ScriptsFragment = graphql(`
44
fragment ScriptsFragment on Content {
5-
scripts(first: 50, filters: { visibilities: [ALL_PAGES, STOREFRONT] }) {
6-
edges {
7-
node {
8-
__typename
9-
integrityHashes {
10-
hash
11-
}
12-
entityId
13-
consentCategory
14-
location
15-
visibility
16-
... on InlineScript {
17-
scriptTag
18-
}
19-
... on SrcScript {
20-
src
21-
}
5+
headerScripts: scripts(
6+
first: 50
7+
filters: { visibilities: [ALL_PAGES, STOREFRONT], location: HEAD }
8+
) {
9+
...ScriptTypeConnectionFragment
10+
}
11+
footerScripts: scripts(
12+
first: 50
13+
filters: { visibilities: [ALL_PAGES, STOREFRONT], location: FOOTER }
14+
) {
15+
...ScriptTypeConnectionFragment
16+
}
17+
}
18+
19+
fragment ScriptTypeConnectionFragment on ScriptTypeConnection {
20+
edges {
21+
node {
22+
__typename
23+
integrityHashes {
24+
hash
25+
}
26+
entityId
27+
consentCategory
28+
location
29+
visibility
30+
... on InlineScript {
31+
scriptTag
32+
}
33+
... on SrcScript {
34+
src
2235
}
2336
}
2437
}

core/components/scripts/scripts.tsx

Lines changed: 3 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -9,26 +9,18 @@ import { ScriptsFragment } from './fragment';
99
type ScriptsData = FragmentOf<typeof ScriptsFragment>;
1010

1111
interface ScriptRendererProps {
12-
scripts: ScriptsData['scripts'] | null;
13-
location: 'head' | 'footer';
12+
scripts: ScriptsData['headerScripts'] | ScriptsData['footerScripts'] | null;
1413
strategy: ComponentProps<typeof Script>['strategy'];
1514
}
1615

17-
export const ScriptManagerScripts = ({ scripts, location, strategy }: ScriptRendererProps) => {
16+
export const ScriptManagerScripts = ({ scripts, strategy }: ScriptRendererProps) => {
1817
if (!scripts?.edges) return null;
1918

2019
const scriptNodes = removeEdgesAndNodes(scripts);
21-
const locationScripts = scriptNodes.filter(
22-
(script) => script.location.toLowerCase() === location,
23-
);
24-
const filteredScripts = locationScripts.filter(
25-
// only render scripts that are visible on all pages or storefront
26-
(script) => script.visibility === 'ALL_PAGES' || script.visibility === 'STOREFRONT',
27-
);
2820

2921
return (
3022
<>
31-
{filteredScripts.map((script) => {
23+
{scriptNodes.map((script) => {
3224
const scriptProps: ComponentProps<typeof Script> = {
3325
strategy,
3426
};

0 commit comments

Comments
 (0)