@prismetic/next-preview-core 1.0.1 → 1.0.3
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/dist/index.d.mts +3 -1
- package/dist/index.d.ts +3 -1
- package/dist/index.js +6 -2
- package/dist/index.mjs +6 -2
- package/package.json +1 -1
- package/src/PreviewProviders.tsx +12 -2
- package/integration_guide.md +0 -151
package/dist/index.d.mts
CHANGED
|
@@ -10,10 +10,12 @@ import React$1 from 'react';
|
|
|
10
10
|
* fetched once will be served from the cache on subsequent page navigations.
|
|
11
11
|
*
|
|
12
12
|
* @param {number} [staleTime=300000] - How long (ms) cached data is considered fresh. Default: 5 minutes.
|
|
13
|
+
* @param {boolean} [refetchOnWindowFocus=false] - Whether to refetch when the browser tab regains focus. Set to true in preview to auto-refresh after editing in Strapi.
|
|
13
14
|
*/
|
|
14
|
-
declare function PreviewProviders({ children, staleTime }: {
|
|
15
|
+
declare function PreviewProviders({ children, staleTime, refetchOnWindowFocus, }: {
|
|
15
16
|
children: React.ReactNode;
|
|
16
17
|
staleTime?: number;
|
|
18
|
+
refetchOnWindowFocus?: boolean;
|
|
17
19
|
}): react_jsx_runtime.JSX.Element;
|
|
18
20
|
|
|
19
21
|
/**
|
package/dist/index.d.ts
CHANGED
|
@@ -10,10 +10,12 @@ import React$1 from 'react';
|
|
|
10
10
|
* fetched once will be served from the cache on subsequent page navigations.
|
|
11
11
|
*
|
|
12
12
|
* @param {number} [staleTime=300000] - How long (ms) cached data is considered fresh. Default: 5 minutes.
|
|
13
|
+
* @param {boolean} [refetchOnWindowFocus=false] - Whether to refetch when the browser tab regains focus. Set to true in preview to auto-refresh after editing in Strapi.
|
|
13
14
|
*/
|
|
14
|
-
declare function PreviewProviders({ children, staleTime }: {
|
|
15
|
+
declare function PreviewProviders({ children, staleTime, refetchOnWindowFocus, }: {
|
|
15
16
|
children: React.ReactNode;
|
|
16
17
|
staleTime?: number;
|
|
18
|
+
refetchOnWindowFocus?: boolean;
|
|
17
19
|
}): react_jsx_runtime.JSX.Element;
|
|
18
20
|
|
|
19
21
|
/**
|
package/dist/index.js
CHANGED
|
@@ -30,13 +30,17 @@ module.exports = __toCommonJS(index_exports);
|
|
|
30
30
|
var import_react_query = require("@tanstack/react-query");
|
|
31
31
|
var import_react = require("react");
|
|
32
32
|
var import_jsx_runtime = require("react/jsx-runtime");
|
|
33
|
-
function PreviewProviders({
|
|
33
|
+
function PreviewProviders({
|
|
34
|
+
children,
|
|
35
|
+
staleTime = 5 * 60 * 1e3,
|
|
36
|
+
refetchOnWindowFocus = false
|
|
37
|
+
}) {
|
|
34
38
|
const [queryClient] = (0, import_react.useState)(
|
|
35
39
|
() => new import_react_query.QueryClient({
|
|
36
40
|
defaultOptions: {
|
|
37
41
|
queries: {
|
|
38
42
|
staleTime,
|
|
39
|
-
refetchOnWindowFocus
|
|
43
|
+
refetchOnWindowFocus,
|
|
40
44
|
retry: 1
|
|
41
45
|
}
|
|
42
46
|
}
|
package/dist/index.mjs
CHANGED
|
@@ -4,13 +4,17 @@
|
|
|
4
4
|
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
|
|
5
5
|
import { useState } from "react";
|
|
6
6
|
import { jsx } from "react/jsx-runtime";
|
|
7
|
-
function PreviewProviders({
|
|
7
|
+
function PreviewProviders({
|
|
8
|
+
children,
|
|
9
|
+
staleTime = 5 * 60 * 1e3,
|
|
10
|
+
refetchOnWindowFocus = false
|
|
11
|
+
}) {
|
|
8
12
|
const [queryClient] = useState(
|
|
9
13
|
() => new QueryClient({
|
|
10
14
|
defaultOptions: {
|
|
11
15
|
queries: {
|
|
12
16
|
staleTime,
|
|
13
|
-
refetchOnWindowFocus
|
|
17
|
+
refetchOnWindowFocus,
|
|
14
18
|
retry: 1
|
|
15
19
|
}
|
|
16
20
|
}
|
package/package.json
CHANGED
package/src/PreviewProviders.tsx
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
|
|
1
2
|
"use client";
|
|
2
3
|
|
|
3
4
|
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
|
|
@@ -12,15 +13,24 @@ import { useState } from "react";
|
|
|
12
13
|
* fetched once will be served from the cache on subsequent page navigations.
|
|
13
14
|
*
|
|
14
15
|
* @param {number} [staleTime=300000] - How long (ms) cached data is considered fresh. Default: 5 minutes.
|
|
16
|
+
* @param {boolean} [refetchOnWindowFocus=false] - Whether to refetch when the browser tab regains focus. Set to true in preview to auto-refresh after editing in Strapi.
|
|
15
17
|
*/
|
|
16
|
-
export function PreviewProviders({
|
|
18
|
+
export function PreviewProviders({
|
|
19
|
+
children,
|
|
20
|
+
staleTime = 5 * 60 * 1000,
|
|
21
|
+
refetchOnWindowFocus = false,
|
|
22
|
+
}: {
|
|
23
|
+
children: React.ReactNode;
|
|
24
|
+
staleTime?: number;
|
|
25
|
+
refetchOnWindowFocus?: boolean;
|
|
26
|
+
}) {
|
|
17
27
|
const [queryClient] = useState(
|
|
18
28
|
() =>
|
|
19
29
|
new QueryClient({
|
|
20
30
|
defaultOptions: {
|
|
21
31
|
queries: {
|
|
22
32
|
staleTime,
|
|
23
|
-
refetchOnWindowFocus
|
|
33
|
+
refetchOnWindowFocus,
|
|
24
34
|
retry: 1,
|
|
25
35
|
},
|
|
26
36
|
},
|
package/integration_guide.md
DELETED
|
@@ -1,151 +0,0 @@
|
|
|
1
|
-
# Implementing Hybrid Static/Preview Architecture
|
|
2
|
-
|
|
3
|
-
This guide explains how to implement the `@prismetic/next-preview-core` architecture into any new or existing Next.js + Strapi project.
|
|
4
|
-
|
|
5
|
-
## 🔴 The Problem
|
|
6
|
-
Modern Next.js applications often use `output: "export"` to build pure static HTML pages for maximum performance and security. However, this creates a major pain point for content editors: **Previewing content.**
|
|
7
|
-
- During a static build, the server fetches data from Strapi and bakes it into HTML.
|
|
8
|
-
- If an editor changes content in Strapi, they cannot see their changes until the entire site is rebuilt via CI/CD, which takes minutes.
|
|
9
|
-
- To provide a live preview, a persistent Node.js server is typically required, defeating the cost/security benefits of a purely static S3/CDN deployment.
|
|
10
|
-
|
|
11
|
-
## 🟢 The Solution
|
|
12
|
-
A **Hybrid Architecture**. We maintain two separate build outputs using an environment variable (`NEXT_PUBLIC_APP_ENV`):
|
|
13
|
-
1. **Production Build (`production`)**: Normal static export. Data is fetched at build time. Deployed to the production S3 bucket.
|
|
14
|
-
2. **Preview Build (`preview`)**: A unique static export. Instead of fetching data at build time, it builds static "shell" components. When the editor loads the page in the browser, these shells use React Query (`@tanstack/react-query`) to fetch the latest draft content *directly from the Strapi GraphQL API* on the client-side.
|
|
15
|
-
|
|
16
|
-
This provides the best of both worlds: highly secure static production sites, and instant live previews hosted on S3 that require zero backend compute.
|
|
17
|
-
|
|
18
|
-
---
|
|
19
|
-
|
|
20
|
-
## 🚀 Step-by-Step Implementation Guide
|
|
21
|
-
|
|
22
|
-
Follow these steps when setting up a new Next.js project.
|
|
23
|
-
|
|
24
|
-
### Step 1: Install Dependencies
|
|
25
|
-
|
|
26
|
-
```bash
|
|
27
|
-
npm install @prismetic/next-preview-core @tanstack/react-query
|
|
28
|
-
```
|
|
29
|
-
|
|
30
|
-
### Step 2: Update Build Scripts
|
|
31
|
-
In your [package.json](file:///Users/sunnyjhunjhunwala/PSM/repos/psm/next-preview-core/package.json), add a script for the preview build:
|
|
32
|
-
|
|
33
|
-
```json
|
|
34
|
-
"scripts": {
|
|
35
|
-
"build": "next build",
|
|
36
|
-
"build:preview": "NEXT_PUBLIC_APP_ENV=preview next build"
|
|
37
|
-
}
|
|
38
|
-
```
|
|
39
|
-
|
|
40
|
-
### Step 3: Add the Provider to the Root Layout
|
|
41
|
-
Wrap your preview environment in [PreviewProviders](file:///Users/sunnyjhunjhunwala/PSM/repos/psm/next-preview-core/src/PreviewProviders.tsx#6-36) to enable React Query caching. This ensures global data (like Navbar/Footer) is fetched once and cached during client-side navigation.
|
|
42
|
-
|
|
43
|
-
**[src/app/[locale]/layout.jsx](file:///Users/sunnyjhunjhunwala/PSM/repos/RedSeaHospitality/turtle-bay-microsite/src/app/%5Blocale%5D/layout.jsx)**
|
|
44
|
-
```jsx
|
|
45
|
-
import { PreviewProviders } from "@prismetic/next-preview-core";
|
|
46
|
-
// ... import your static Header/Footer and dynamic PreviewLayoutFetcher
|
|
47
|
-
|
|
48
|
-
export default async function LocaleLayout({ children, params }) {
|
|
49
|
-
const { locale } = await params;
|
|
50
|
-
const isPreview = process.env.NEXT_PUBLIC_APP_ENV === "preview";
|
|
51
|
-
|
|
52
|
-
if (isPreview) {
|
|
53
|
-
return (
|
|
54
|
-
<PreviewProviders>
|
|
55
|
-
<PreviewLayoutFetcher locale={locale}>
|
|
56
|
-
{children}
|
|
57
|
-
</PreviewLayoutFetcher>
|
|
58
|
-
</PreviewProviders>
|
|
59
|
-
);
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
// Production: fetch statically
|
|
63
|
-
const layoutData = await fetchLayoutData(locale);
|
|
64
|
-
return (
|
|
65
|
-
<>
|
|
66
|
-
<Header data={layoutData.header} />
|
|
67
|
-
<main>{children}</main>
|
|
68
|
-
<Footer data={layoutData.footer} />
|
|
69
|
-
</>
|
|
70
|
-
);
|
|
71
|
-
}
|
|
72
|
-
```
|
|
73
|
-
|
|
74
|
-
### Step 4: Separate Pure UI from Page Logic
|
|
75
|
-
Never put data fetching and UI rendering in the same component. Extract the presentation layer into its own file (e.g., [HomeUI.jsx](file:///Users/sunnyjhunjhunwala/PSM/repos/RedSeaHospitality/turtle-bay-microsite/src/app/%5Blocale%5D/HomeUI.jsx), [DynamicPageUI.jsx](file:///Users/sunnyjhunjhunwala/PSM/repos/RedSeaHospitality/turtle-bay-microsite/src/app/%5Blocale%5D/%5B...slug%5D/DynamicPageUI.jsx)).
|
|
76
|
-
|
|
77
|
-
**[src/app/[locale]/HomeUI.jsx](file:///Users/sunnyjhunjhunwala/PSM/repos/RedSeaHospitality/turtle-bay-microsite/src/app/%5Blocale%5D/HomeUI.jsx)**
|
|
78
|
-
```jsx
|
|
79
|
-
// A pure function that just takes data and renders standard components
|
|
80
|
-
export default function HomeUI({ homepageData }) {
|
|
81
|
-
return (
|
|
82
|
-
<div>
|
|
83
|
-
<Hero data={homepageData.hero} />
|
|
84
|
-
<BlockManager blocks={homepageData.blocks} />
|
|
85
|
-
</div>
|
|
86
|
-
);
|
|
87
|
-
}
|
|
88
|
-
```
|
|
89
|
-
|
|
90
|
-
### Step 5: Create the Preview Wrapper
|
|
91
|
-
Use the [withLivePreview](file:///Users/sunnyjhunjhunwala/PSM/repos/psm/next-preview-core/src/withLivePreview.tsx#5-50) HOC to wrap the pure UI component. Give it your data fetching function and a unique cache key.
|
|
92
|
-
|
|
93
|
-
**[src/components/preview/PreviewHome.jsx](file:///Users/sunnyjhunjhunwala/PSM/repos/RedSeaHospitality/turtle-bay-microsite/src/components/preview/PreviewHome.jsx)**
|
|
94
|
-
```jsx
|
|
95
|
-
"use client";
|
|
96
|
-
|
|
97
|
-
import { withLivePreview } from "@prismetic/next-preview-core";
|
|
98
|
-
import HomeUI from "@/app/[locale]/HomeUI";
|
|
99
|
-
import { fetchHomepageContent } from "@/api/queryModules";
|
|
100
|
-
|
|
101
|
-
export const PreviewHome = withLivePreview(
|
|
102
|
-
HomeUI, // The pure UI
|
|
103
|
-
({ locale }) => fetchHomepageContent(locale), // The data fetcher
|
|
104
|
-
({ locale }) => ["homepage", locale] // The React Query cache key
|
|
105
|
-
);
|
|
106
|
-
```
|
|
107
|
-
|
|
108
|
-
### Step 6: Update the Page Route
|
|
109
|
-
Conditionally render the Preview Wrapper or the Static UI based on the environment variable.
|
|
110
|
-
|
|
111
|
-
**[src/app/[locale]/page.jsx](file:///Users/sunnyjhunjhunwala/PSM/repos/RedSeaHospitality/turtle-bay-microsite/src/app/%5Blocale%5D/page.jsx)**
|
|
112
|
-
```jsx
|
|
113
|
-
import { PreviewHome } from "@/components/preview/PreviewHome";
|
|
114
|
-
import HomeUI from "./HomeUI";
|
|
115
|
-
import { fetchHomepageContent } from "@/api/queryModules";
|
|
116
|
-
|
|
117
|
-
export default async function LocaleHomePage({ params }) {
|
|
118
|
-
const { locale } = await params;
|
|
119
|
-
const isPreview = process.env.NEXT_PUBLIC_APP_ENV === "preview";
|
|
120
|
-
|
|
121
|
-
// If preview mode, return the client-side fetching shell
|
|
122
|
-
if (isPreview) return <PreviewHome locale={locale} />;
|
|
123
|
-
|
|
124
|
-
// Production mode: fetch statically during build
|
|
125
|
-
const data = await fetchHomepageContent(locale);
|
|
126
|
-
return <HomeUI {...data} />; // Make sure to match prop names
|
|
127
|
-
}
|
|
128
|
-
```
|
|
129
|
-
|
|
130
|
-
### Step 7: Strapi CORS Configuration
|
|
131
|
-
For preview mode to work, the browser must be able to make a request directly to Strapi. Ensure your Strapi server's `config/middlewares.js` has appropriate CORS rules allowing the preview frontend's domain.
|
|
132
|
-
|
|
133
|
-
```javascript
|
|
134
|
-
// Example Strapi CORS config
|
|
135
|
-
{
|
|
136
|
-
name: 'strapi::cors',
|
|
137
|
-
config: {
|
|
138
|
-
origin: ['https://preview.yourproject.com', 'http://localhost:3000'],
|
|
139
|
-
methods: ['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'HEAD', 'OPTIONS'],
|
|
140
|
-
headers: ['Content-Type', 'Authorization', 'Origin', 'Accept'],
|
|
141
|
-
keepHeaderOnError: true,
|
|
142
|
-
},
|
|
143
|
-
}
|
|
144
|
-
```
|
|
145
|
-
|
|
146
|
-
---
|
|
147
|
-
|
|
148
|
-
## ✅ Final Validation
|
|
149
|
-
1. Run `npm run build:preview`.
|
|
150
|
-
2. Inspect [out/index.html](file:///Users/sunnyjhunjhunwala/PSM/repos/RedSeaHospitality/turtle-bay-microsite/out/index.html) — it should be a nearly empty shell importing JavaScript bundles, not fully populated content.
|
|
151
|
-
3. Serve [out/](file:///Users/sunnyjhunjhunwala/PSM/repos/RedSeaHospitality/turtle-bay-microsite/src/app/%5Blocale%5D/layout.jsx#16-50) locally and view the Network tab in your browser — you should see GraphQL requests fetching data natively on page load.
|