@prismetic/next-preview-core 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +118 -0
- package/package.json +20 -0
- package/src/PreviewProviders.tsx +35 -0
- package/src/index.ts +6 -0
- package/src/withLivePreview.tsx +49 -0
package/README.md
ADDED
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
# @prismetic/next-preview-core
|
|
2
|
+
|
|
3
|
+
Hybrid static/preview architecture for Next.js + Strapi projects.
|
|
4
|
+
|
|
5
|
+
Enables a production build that is a pure static export and a preview build hosted on S3 that fetches live Strapi content directly from the browser on page load — no Node.js server required.
|
|
6
|
+
|
|
7
|
+
## Installation
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npm install @prismetic/next-preview-core
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
Also requires `@tanstack/react-query` as a peer dependency:
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
npm install @tanstack/react-query
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## Exports
|
|
20
|
+
|
|
21
|
+
### `PreviewProviders`
|
|
22
|
+
|
|
23
|
+
Wrap your preview layout in this provider to enable React Query caching. Layout data (Navbar, Footer) fetched once will not refetch on page navigation.
|
|
24
|
+
|
|
25
|
+
```jsx
|
|
26
|
+
// src/app/[locale]/layout.jsx
|
|
27
|
+
import { PreviewProviders } from "@prismetic/next-preview-core";
|
|
28
|
+
|
|
29
|
+
const isPreview = process.env.NEXT_PUBLIC_APP_ENV === "preview";
|
|
30
|
+
|
|
31
|
+
return isPreview ? (
|
|
32
|
+
<PreviewProviders>
|
|
33
|
+
<YourPreviewLayoutFetcher locale={locale}>
|
|
34
|
+
{children}
|
|
35
|
+
</YourPreviewLayoutFetcher>
|
|
36
|
+
</PreviewProviders>
|
|
37
|
+
) : (
|
|
38
|
+
// ... static layout
|
|
39
|
+
);
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
### `withLivePreview(WrappedComponent, fetcherFn, cacheKeyGenerator)`
|
|
43
|
+
|
|
44
|
+
A Higher-Order Component that handles `useQuery`, loading/error states, and injects fetched data into your UI component.
|
|
45
|
+
|
|
46
|
+
| Argument | Type | Description |
|
|
47
|
+
|---|---|---|
|
|
48
|
+
| `WrappedComponent` | `React.Component` | Your pure UI component |
|
|
49
|
+
| `fetcherFn` | `async (props) => data` | Async function that fetches the data |
|
|
50
|
+
| `cacheKeyGenerator` | `(props) => string[]` | Returns the React Query cache key array |
|
|
51
|
+
|
|
52
|
+
```jsx
|
|
53
|
+
// src/components/preview/PreviewHome.jsx
|
|
54
|
+
"use client";
|
|
55
|
+
|
|
56
|
+
import { withLivePreview } from "@prismetic/next-preview-core";
|
|
57
|
+
import HomeUI from "@/app/[locale]/HomeUI";
|
|
58
|
+
import { fetchHomepageContent } from "@/api/queryModules";
|
|
59
|
+
|
|
60
|
+
export const PreviewHome = withLivePreview(
|
|
61
|
+
HomeUI,
|
|
62
|
+
({ locale }) => fetchHomepageContent(locale),
|
|
63
|
+
({ locale }) => ["homepage", locale]
|
|
64
|
+
);
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
Then in your `page.jsx`:
|
|
68
|
+
|
|
69
|
+
```jsx
|
|
70
|
+
// src/app/[locale]/page.jsx
|
|
71
|
+
import { PreviewHome } from "@/components/preview/PreviewHome";
|
|
72
|
+
|
|
73
|
+
export default async function LocaleHomePage({ params }) {
|
|
74
|
+
const { locale } = await params;
|
|
75
|
+
const isPreview = process.env.NEXT_PUBLIC_APP_ENV === "preview";
|
|
76
|
+
|
|
77
|
+
if (isPreview) return <PreviewHome locale={locale} />;
|
|
78
|
+
|
|
79
|
+
// Production: fetch data server-side
|
|
80
|
+
const data = await fetchHomepageContent(locale);
|
|
81
|
+
return <HomeUI data={data} />;
|
|
82
|
+
}
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
## Build Commands
|
|
86
|
+
|
|
87
|
+
| Command | Output |
|
|
88
|
+
|---|---|
|
|
89
|
+
| `npm run build` | Production static export (data baked in at build time) |
|
|
90
|
+
| `npm run build:preview` | Preview static export (client-side fetch on page load) |
|
|
91
|
+
|
|
92
|
+
Add to your `package.json`:
|
|
93
|
+
|
|
94
|
+
```json
|
|
95
|
+
{
|
|
96
|
+
"scripts": {
|
|
97
|
+
"build": "next build",
|
|
98
|
+
"build:preview": "NEXT_PUBLIC_APP_ENV=preview next build"
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
## Local Development (npm link)
|
|
104
|
+
|
|
105
|
+
To test changes to this package in a project before publishing:
|
|
106
|
+
|
|
107
|
+
```bash
|
|
108
|
+
# 1. In this package directory
|
|
109
|
+
npm link
|
|
110
|
+
|
|
111
|
+
# 2. In your project directory
|
|
112
|
+
npm link @prismetic/next-preview-core
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
To unlink:
|
|
116
|
+
```bash
|
|
117
|
+
npm unlink @prismetic/next-preview-core
|
|
118
|
+
```
|
package/package.json
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@prismetic/next-preview-core",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Hybrid static/preview architecture for Next.js + Strapi projects",
|
|
5
|
+
"main": "./src/index.ts",
|
|
6
|
+
"exports": {
|
|
7
|
+
".": "./src/index.ts"
|
|
8
|
+
},
|
|
9
|
+
"keywords": [
|
|
10
|
+
"next.js",
|
|
11
|
+
"strapi",
|
|
12
|
+
"preview",
|
|
13
|
+
"static"
|
|
14
|
+
],
|
|
15
|
+
"license": "MIT",
|
|
16
|
+
"peerDependencies": {
|
|
17
|
+
"react": ">=18",
|
|
18
|
+
"@tanstack/react-query": ">=5"
|
|
19
|
+
}
|
|
20
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
|
|
4
|
+
import { useState } from "react";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* React Query provider for Preview Mode.
|
|
8
|
+
* Part of @prismetic/next-preview-core.
|
|
9
|
+
*
|
|
10
|
+
* Wrap the preview build's component tree in this provider to enable
|
|
11
|
+
* client-side caching of Strapi data. Layout data (Navbar, Footer)
|
|
12
|
+
* fetched once will be served from the cache on subsequent page navigations.
|
|
13
|
+
*
|
|
14
|
+
* @param {number} [staleTime=300000] - How long (ms) cached data is considered fresh. Default: 5 minutes.
|
|
15
|
+
*/
|
|
16
|
+
export function PreviewProviders({ children, staleTime = 5 * 60 * 1000 }) {
|
|
17
|
+
const [queryClient] = useState(
|
|
18
|
+
() =>
|
|
19
|
+
new QueryClient({
|
|
20
|
+
defaultOptions: {
|
|
21
|
+
queries: {
|
|
22
|
+
staleTime,
|
|
23
|
+
refetchOnWindowFocus: false,
|
|
24
|
+
retry: 1,
|
|
25
|
+
},
|
|
26
|
+
},
|
|
27
|
+
})
|
|
28
|
+
);
|
|
29
|
+
|
|
30
|
+
return (
|
|
31
|
+
<QueryClientProvider client={queryClient}>
|
|
32
|
+
{children}
|
|
33
|
+
</QueryClientProvider>
|
|
34
|
+
);
|
|
35
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { useQuery } from "@tanstack/react-query";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* A generic Higher-Order Component for Preview Mode data fetching.
|
|
7
|
+
* Part of @prismetic/next-preview-core.
|
|
8
|
+
*
|
|
9
|
+
* @param {React.Component} WrappedComponent - The pure UI component to render once data is fetched.
|
|
10
|
+
* @param {Function} fetcherFn - An async function that fetches data (receives component props as argument).
|
|
11
|
+
* @param {Function} cacheKeyGenerator - A function that returns a React Query queryKey array (receives component props).
|
|
12
|
+
*
|
|
13
|
+
* @example
|
|
14
|
+
* // In your project's preview wrapper:
|
|
15
|
+
* import { withLivePreview } from "@prismetic/next-preview-core";
|
|
16
|
+
* import HomeUI from "../HomeUI";
|
|
17
|
+
* import { fetchHomepageContent } from "@/api/queryModules";
|
|
18
|
+
*
|
|
19
|
+
* export const PreviewHome = withLivePreview(
|
|
20
|
+
* HomeUI,
|
|
21
|
+
* ({ locale }) => fetchHomepageContent(locale),
|
|
22
|
+
* ({ locale }) => ["homepage", locale]
|
|
23
|
+
* );
|
|
24
|
+
*/
|
|
25
|
+
export function withLivePreview(WrappedComponent, fetcherFn, cacheKeyGenerator) {
|
|
26
|
+
return function PreviewComponent(props) {
|
|
27
|
+
const queryKey = cacheKeyGenerator(props);
|
|
28
|
+
|
|
29
|
+
const { data, isLoading } = useQuery({
|
|
30
|
+
queryKey,
|
|
31
|
+
queryFn: () => fetcherFn(props),
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
if (isLoading) {
|
|
35
|
+
return (
|
|
36
|
+
<div style={{ minHeight: "100vh", display: "flex", alignItems: "center", justifyContent: "center" }}>
|
|
37
|
+
Loading preview...
|
|
38
|
+
</div>
|
|
39
|
+
);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
if (!data) {
|
|
43
|
+
return <div>Page not found</div>;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// Props spread first, then fetched data takes priority
|
|
47
|
+
return <WrappedComponent {...props} {...data} />;
|
|
48
|
+
};
|
|
49
|
+
}
|