@prismetic/next-preview-core 1.0.1 → 1.0.2

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@prismetic/next-preview-core",
3
- "version": "1.0.1",
3
+ "version": "1.0.2",
4
4
  "description": "Hybrid static/preview architecture for Next.js + Strapi projects",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.mjs",
@@ -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({ children, staleTime = 5 * 60 * 1000 }: { children: React.ReactNode; staleTime?: number }) {
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: false,
33
+ refetchOnWindowFocus,
24
34
  retry: 1,
25
35
  },
26
36
  },
@@ -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.