hydrogen-sanity 3.3.1 → 4.0.0-beta.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/README.md +290 -172
- package/dist/index.d.ts +53 -109
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1 -1
- package/dist/index.mjs.map +1 -1
- package/package.json +4 -3
- package/src/index.ts +4 -2
- package/src/loader.ts +151 -0
- package/src/previewRoute.ts +44 -0
- package/src/types.ts +24 -6
- package/src/client.ts +0 -155
- package/src/preview/PreviewProvider.tsx +0 -51
- package/src/preview/PreviewSession.ts +0 -54
- package/src/preview/SanityPreview.tsx +0 -55
- package/src/preview/context.tsx +0 -6
- package/src/preview/getPreview.ts +0 -12
- package/src/preview/index.ts +0 -5
package/README.md
CHANGED
|
@@ -2,14 +2,33 @@
|
|
|
2
2
|
|
|
3
3
|
[Sanity.io](https://www.sanity.io) toolkit for [Hydrogen](https://hydrogen.shopify.dev/). Requires `@shopify/hydrogen >= 2023.7.0`.
|
|
4
4
|
|
|
5
|
+
- [hydrogen-sanity](#hydrogen-sanity)
|
|
6
|
+
- [Installation](#installation)
|
|
7
|
+
- [Usage](#usage)
|
|
8
|
+
- [Satisfy TypeScript](#satisfy-typescript)
|
|
9
|
+
- [Interacting with Sanity data](#interacting-with-sanity-data)
|
|
10
|
+
- [Preferred: Cached fetches using `loadQuery`](#preferred-cached-fetches-using-loadquery)
|
|
11
|
+
- [`loadQuery` Request Options](#loadquery-request-options)
|
|
12
|
+
- [Alternatively: Using `client` directly](#alternatively-using-client-directly)
|
|
13
|
+
- [Visual Editing](#visual-editing)
|
|
14
|
+
- [Enabling preview mode](#enabling-preview-mode)
|
|
15
|
+
- [Setup CORS for front-end domains](#setup-cors-for-front-end-domains)
|
|
16
|
+
- [Modify Content Security Policy for Studio domains](#modify-content-security-policy-for-studio-domains)
|
|
17
|
+
- [Setup Presentation Tool](#setup-presentation-tool)
|
|
18
|
+
- [Using `@sanity/client` instead of hydrogen-sanity](#using-sanityclient-instead-of-hydrogen-sanity)
|
|
19
|
+
- [Migrate to v4 from v3](#migrate-to-v4-from-v3)
|
|
20
|
+
- [License](#license)
|
|
21
|
+
- [Develop \& test](#develop--test)
|
|
22
|
+
- [Release new version](#release-new-version)
|
|
23
|
+
|
|
5
24
|
**Features:**
|
|
6
25
|
|
|
7
|
-
- Cacheable queries to Sanity
|
|
8
|
-
-
|
|
26
|
+
- Cacheable queries to [Sanity API CDN](https://www.sanity.io/docs/api-cdn)
|
|
27
|
+
- Interactive live preview with [Visual Editing](https://www.sanity.io/docs/loaders-and-overlays)
|
|
9
28
|
|
|
10
29
|
> **Note**
|
|
11
30
|
>
|
|
12
|
-
> Using this package isn't strictly required for working with Sanity in a Hydrogen storefront. If you'd like to use `@sanity/client` directly, see [Using `@sanity/client` directly](#using-sanityclient-directly) below.
|
|
31
|
+
> Using this package isn't strictly required for working with Sanity in a Hydrogen storefront. If you'd like to use `@sanity/react-loader` and/or `@sanity/client` directly, see [Using `@sanity/client` directly](#using-sanityclient-directly) below.
|
|
13
32
|
|
|
14
33
|
## Installation
|
|
15
34
|
|
|
@@ -27,78 +46,57 @@ pnpm install hydrogen-sanity
|
|
|
27
46
|
|
|
28
47
|
## Usage
|
|
29
48
|
|
|
30
|
-
Update the server file to include the Sanity
|
|
49
|
+
Update the server file to include the Sanity Loader, and optionally, configure the preview mode if you plan to setup Visual Editing
|
|
31
50
|
|
|
32
51
|
```ts
|
|
33
52
|
// ./server.ts
|
|
34
53
|
|
|
35
54
|
// ...all other imports
|
|
36
|
-
|
|
55
|
+
// Add imports for Sanity Loader and Preview Session
|
|
56
|
+
import {createSanityLoader} from 'hydrogen-sanity'
|
|
37
57
|
|
|
38
58
|
// Inside the default export
|
|
39
59
|
export default () => {
|
|
60
|
+
// ... Leave all other functions like the storefront client as-is
|
|
40
61
|
|
|
41
|
-
// 1.
|
|
42
|
-
const
|
|
43
|
-
|
|
44
|
-
caches.open('hydrogen'),
|
|
45
|
-
HydrogenSession.init(request, secrets),
|
|
46
|
-
// 👇 Add preview session
|
|
47
|
-
(async function createPreviewSession() {
|
|
48
|
-
const storage = createCookieSessionStorage({
|
|
49
|
-
cookie: {
|
|
50
|
-
name: '__preview',
|
|
51
|
-
httpOnly: true,
|
|
52
|
-
sameSite: true,
|
|
53
|
-
secrets,
|
|
54
|
-
},
|
|
55
|
-
});
|
|
56
|
-
|
|
57
|
-
const session = await storage.getSession(request.headers.get("Cookie"));
|
|
58
|
-
|
|
59
|
-
return new HydrogenSession(storage, session);
|
|
60
|
-
})(),
|
|
61
|
-
]);
|
|
62
|
-
|
|
63
|
-
// Leave all other functions like the storefront client as-is
|
|
64
|
-
const {storefront} = createStorefrontClient({ ... })
|
|
65
|
-
|
|
66
|
-
// 2. Add the Sanity client
|
|
67
|
-
const sanity = createSanityClient({
|
|
62
|
+
// 1. Configure the Sanity Loader and preview mode
|
|
63
|
+
const sanity = createSanityLoader({
|
|
64
|
+
// Required:
|
|
68
65
|
cache,
|
|
69
66
|
waitUntil,
|
|
70
|
-
//
|
|
71
|
-
preview:
|
|
72
|
-
env.SANITY_PREVIEW_SECRET && env.SANITY_API_TOKEN
|
|
73
|
-
? {
|
|
74
|
-
session: previewSession,
|
|
75
|
-
token: env.SANITY_API_TOKEN,
|
|
76
|
-
// Optionally, provide an alternative to the default `previewDrafts` perspective when in preview mode
|
|
77
|
-
// See https://www.sanity.io/docs/perspectives
|
|
78
|
-
// perspective: "raw"
|
|
79
|
-
}
|
|
80
|
-
: undefined,
|
|
67
|
+
// Required:
|
|
81
68
|
// Pass configuration options for Sanity client
|
|
82
69
|
config: {
|
|
83
70
|
projectId: env.SANITY_PROJECT_ID,
|
|
84
71
|
dataset: env.SANITY_DATASET,
|
|
85
72
|
apiVersion: env.SANITY_API_VERSION ?? '2023-03-30',
|
|
86
73
|
useCdn: process.env.NODE_ENV === 'production',
|
|
87
|
-
}
|
|
88
|
-
|
|
74
|
+
},
|
|
75
|
+
// Optionally, set a global default cache strategy, defaults to CacheLong
|
|
76
|
+
// strategy: CacheShort() | null,
|
|
77
|
+
// Optionally, enable Visual Editing
|
|
78
|
+
// See "Visual Editing" section below to setup the preview route
|
|
79
|
+
preview:
|
|
80
|
+
session.get('projectId') === env.SANITY_PROJECT_ID
|
|
81
|
+
? {token: env.SANITY_API_TOKEN, studioUrl: 'http://localhost:3333'}
|
|
82
|
+
: undefined,
|
|
83
|
+
})
|
|
89
84
|
|
|
90
|
-
//
|
|
85
|
+
// 2. Make Sanity available to all action and loader contexts
|
|
91
86
|
const handleRequest = createRequestHandler({
|
|
92
87
|
// ...other settings
|
|
93
88
|
getLoadContext: () => ({
|
|
94
89
|
// ...other providers
|
|
95
90
|
sanity,
|
|
96
91
|
}),
|
|
97
|
-
})
|
|
92
|
+
})
|
|
98
93
|
}
|
|
99
94
|
```
|
|
100
95
|
|
|
101
|
-
Update your environment variables with settings from your Sanity project.
|
|
96
|
+
Update your environment variables with settings from your Sanity project.
|
|
97
|
+
|
|
98
|
+
- Copy these from [sanity.io/manage](https://sanity.io/manage)
|
|
99
|
+
- or run `npx sanity@latest init --env` to fill the minimum required values from a new or existing project
|
|
102
100
|
|
|
103
101
|
```sh
|
|
104
102
|
# Project ID
|
|
@@ -107,17 +105,15 @@ SANITY_PROJECT_ID=""
|
|
|
107
105
|
SANITY_DATASET=""
|
|
108
106
|
# (Optional) Sanity API version
|
|
109
107
|
SANITY_API_VERSION=""
|
|
110
|
-
# Sanity token to authenticate requests in "preview" mode
|
|
111
|
-
#
|
|
112
|
-
#
|
|
108
|
+
# Sanity token to authenticate requests in "preview" mode
|
|
109
|
+
# must have `viewer` role or higher access
|
|
110
|
+
# Create in sanity.io/manage
|
|
113
111
|
SANITY_API_TOKEN=""
|
|
114
|
-
# Secret for authenticating preview mode
|
|
115
|
-
SANITY_PREVIEW_SECRET=""
|
|
116
112
|
```
|
|
117
113
|
|
|
118
114
|
### Satisfy TypeScript
|
|
119
115
|
|
|
120
|
-
Update the environment variables in `Env`
|
|
116
|
+
Update the environment variables in `Env` and `AppLoadContext` to include the Sanity configuration:
|
|
121
117
|
|
|
122
118
|
```ts
|
|
123
119
|
// ./remix.env.d.ts
|
|
@@ -128,11 +124,10 @@ declare global {
|
|
|
128
124
|
|
|
129
125
|
interface Env {
|
|
130
126
|
// ...other variables
|
|
131
|
-
SANITY_PREVIEW_SECRET: string
|
|
132
|
-
SANITY_API_TOKEN: string
|
|
133
127
|
SANITY_PROJECT_ID: string
|
|
134
128
|
SANITY_DATASET: string
|
|
135
129
|
SANITY_API_VERSION: string
|
|
130
|
+
SANITY_API_TOKEN: string
|
|
136
131
|
}
|
|
137
132
|
}
|
|
138
133
|
|
|
@@ -144,59 +139,89 @@ declare module '@shopify/remix-oxygen' {
|
|
|
144
139
|
}
|
|
145
140
|
```
|
|
146
141
|
|
|
147
|
-
|
|
142
|
+
## Interacting with Sanity data
|
|
143
|
+
|
|
144
|
+
### Preferred: Cached fetches using `loadQuery`
|
|
148
145
|
|
|
149
|
-
Query Sanity API and cache the response (defaults to `CacheLong` caching strategy)
|
|
146
|
+
Query Sanity's API and use Hydrogen's cache to store the response (defaults to `CacheLong` caching strategy).
|
|
147
|
+
|
|
148
|
+
`loadQuery` will not implement Hydrogen's caching when:
|
|
149
|
+
|
|
150
|
+
- in preview mode or
|
|
151
|
+
- when the Sanity Client config for `useCdn` is false or
|
|
152
|
+
- when the `strategy` option is set to `null`.
|
|
153
|
+
|
|
154
|
+
Learn more about configuring [caching in Hydrogen on the Shopify documentation](https://shopify.dev/docs/custom-storefronts/hydrogen/caching).
|
|
150
155
|
|
|
151
156
|
```ts
|
|
152
|
-
export async function loader({context, params}:
|
|
153
|
-
const
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
id: 'home',
|
|
157
|
-
},
|
|
158
|
-
// optionally pass a caching strategy
|
|
159
|
-
// cache: CacheShort()
|
|
160
|
-
})
|
|
157
|
+
export async function loader({context, params}: LoaderFunctionArgs) {
|
|
158
|
+
const query = `*[_type == "page" && _id == $id][0]`
|
|
159
|
+
const params = {id: 'home'}
|
|
160
|
+
const initial = await context.sanity.loadQuery(query, params)
|
|
161
161
|
|
|
162
|
-
return json({
|
|
163
|
-
homepage,
|
|
164
|
-
})
|
|
162
|
+
return json({initial})
|
|
165
163
|
}
|
|
166
164
|
```
|
|
167
165
|
|
|
168
|
-
|
|
166
|
+
### `loadQuery` Request Options
|
|
167
|
+
|
|
168
|
+
If you need to pass any additional options to the request provide `queryOptions` like so:
|
|
169
169
|
|
|
170
170
|
```ts
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
171
|
+
const page = await context.sanity.loadQuery<HomePage>(query, params, {
|
|
172
|
+
// These additional options will be passed to sanity.loadQuery
|
|
173
|
+
queryOptions: {
|
|
174
|
+
tag: 'home',
|
|
175
|
+
headers: {
|
|
176
|
+
'Accept-Encoding': 'br, gzip, *',
|
|
177
|
+
},
|
|
178
|
+
},
|
|
179
|
+
// Optionally customize the cache strategy for this request
|
|
180
|
+
// strategy: CacheShort(),
|
|
181
|
+
// Or disable caching for this request
|
|
182
|
+
// strategy: null,
|
|
183
|
+
})
|
|
184
|
+
```
|
|
176
185
|
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
186
|
+
### Alternatively: Using `client` directly
|
|
187
|
+
|
|
188
|
+
The Sanity Client is also configured in context, but will not return data in the same shape as `loadQuery`. It is recommended to use `loadQuery` for data fetching.
|
|
189
|
+
|
|
190
|
+
Sanity Client can be used for mutations within actions, for example:
|
|
191
|
+
|
|
192
|
+
```ts
|
|
193
|
+
export async function action({context, request}: ActionFunctionArgs) {
|
|
194
|
+
if (!isAuthenticated(request)) {
|
|
195
|
+
return redirect('/login')
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
return context.sanity
|
|
199
|
+
.withConfig({
|
|
200
|
+
token: context.env.SANITY_WRITE_TOKEN,
|
|
201
|
+
})
|
|
202
|
+
.client.create({
|
|
203
|
+
_type: 'comment',
|
|
204
|
+
text: request.body.get('text'),
|
|
205
|
+
})
|
|
206
|
+
}
|
|
180
207
|
```
|
|
181
208
|
|
|
182
|
-
|
|
209
|
+
## Visual Editing
|
|
183
210
|
|
|
184
|
-
Enable real-time, live preview
|
|
211
|
+
Enable real-time, interactive live preview inside the Presentation Tool of your Sanity Studio.
|
|
185
212
|
|
|
186
|
-
First
|
|
213
|
+
First set up your root route to enable preview mode across the entire application, if the preview session is active:
|
|
187
214
|
|
|
188
215
|
```tsx
|
|
189
216
|
// ./app/root.tsx
|
|
190
217
|
|
|
191
218
|
// ...other imports
|
|
192
|
-
import {
|
|
219
|
+
import {VisualEditing} from '@sanity/visual-editing/remix'
|
|
193
220
|
|
|
194
221
|
export async function loader({context}: LoaderArgs) {
|
|
195
|
-
const preview = getPreview(context)
|
|
196
|
-
|
|
197
222
|
return json({
|
|
198
223
|
// ... other loader data
|
|
199
|
-
preview,
|
|
224
|
+
preview: context.sanity.preview,
|
|
200
225
|
})
|
|
201
226
|
}
|
|
202
227
|
|
|
@@ -212,10 +237,8 @@ export default function App() {
|
|
|
212
237
|
<Links />
|
|
213
238
|
</head>
|
|
214
239
|
<body>
|
|
215
|
-
|
|
216
|
-
<
|
|
217
|
-
<Outlet />
|
|
218
|
-
</Preview>
|
|
240
|
+
<Outlet />
|
|
241
|
+
{preview ? <VisualEditing /> : null}
|
|
219
242
|
<ScrollRestoration />
|
|
220
243
|
<Scripts />
|
|
221
244
|
</body>
|
|
@@ -224,113 +247,107 @@ export default function App() {
|
|
|
224
247
|
}
|
|
225
248
|
```
|
|
226
249
|
|
|
227
|
-
|
|
250
|
+
This Visual Editing component will trigger incremental updates to draft documents from the server for users with a valid preview session. [Duplicate its source](https://github.com/sanity-io/visual-editing/blob/main/packages/visual-editing/src/remix/VisualEditing.tsx) into your own project if you wish to customize its behavior.
|
|
228
251
|
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
```tsx
|
|
232
|
-
import {PreviewLoading} from '~/components/PreviewLoading';
|
|
233
|
-
|
|
234
|
-
// (Optional) pass a string or your own React component to show while data is loading
|
|
235
|
-
<PreviewProvider {...preview} fallback={<PreviewLoading />}>
|
|
236
|
-
```
|
|
237
|
-
|
|
238
|
-
Next, for any route that needs to render a preview, wrap it in a `SanityPreview` component which re-runs the same query client-side but will render draft content in place of published content, if it exists. Updating in real-time as changes are streamed in.
|
|
239
|
-
|
|
240
|
-
The component will be rendered with live preview if the preview session is found, otherwise, it renders the component with static content.
|
|
252
|
+
These updates are faster when your initial server-side content is passed through an optional `useQuery` hook.
|
|
241
253
|
|
|
242
254
|
```tsx
|
|
243
255
|
// Any route file, such as ./app/routes/index.tsx
|
|
244
256
|
|
|
245
257
|
// ...all other imports
|
|
246
|
-
import {
|
|
258
|
+
import {useQuery} from '@sanity/react-loader'
|
|
259
|
+
|
|
260
|
+
export async function loader({context, params}: LoaderArgs) {
|
|
261
|
+
const query = `*[_type == "page" && _id == $id][0]`
|
|
262
|
+
const params = {id: 'home'}
|
|
263
|
+
const initial = await context.sanity.loadQuery(query, params)
|
|
247
264
|
|
|
248
|
-
|
|
249
|
-
|
|
265
|
+
return json({initial, query, params})
|
|
266
|
+
}
|
|
250
267
|
|
|
251
268
|
// Default export where content is rendered
|
|
252
269
|
export default function Index() {
|
|
253
270
|
// Get initial data, passing it as snapshot to render preview...
|
|
254
|
-
const {
|
|
271
|
+
const {initial, query, params} = useLoaderData<typeof loader>()
|
|
272
|
+
// Optional, pass query, params and initial data to useQuery for faster updates
|
|
273
|
+
const {loading, data} = useQuery(query, params, initial)
|
|
255
274
|
|
|
256
|
-
|
|
257
|
-
// content client-side and renders live updates
|
|
258
|
-
// of draft content
|
|
259
|
-
return (
|
|
260
|
-
<SanityPreview
|
|
261
|
-
data={homepage}
|
|
262
|
-
query={`*[_type == "page" && _id == $id][0]`}
|
|
263
|
-
params={{id: 'home'}}
|
|
264
|
-
>
|
|
265
|
-
{(homepage) => <>{/* ...render homepage using data */}</>}
|
|
266
|
-
</SanityPreview>
|
|
267
|
-
)
|
|
275
|
+
return loading ? <div>Loading</div> : <Page page={data} />
|
|
268
276
|
}
|
|
269
277
|
```
|
|
270
278
|
|
|
271
|
-
###
|
|
279
|
+
### Enabling preview mode
|
|
280
|
+
|
|
281
|
+
For users to enter preview mode, they will need to visit a route that performs some authentication and then writes to the session.
|
|
272
282
|
|
|
273
|
-
|
|
283
|
+
`hydrogen-sanity` comes with a preconfigured route for this purpose. It checks the value of a secret in the URL used by Presentation Tool - and if valid - writes the `projectId` to the Hydrogen session.
|
|
284
|
+
|
|
285
|
+
Add this route to your project like below, or view the source to copy and modify it in your project.
|
|
274
286
|
|
|
275
287
|
```tsx
|
|
276
|
-
// ./app/routes/
|
|
277
|
-
import {LoaderFunction, redirect} from '@shopify/remix-oxygen'
|
|
278
|
-
|
|
279
|
-
export const loader: LoaderFunction = async function ({request, context}) {
|
|
280
|
-
const {env, sanity} = context
|
|
281
|
-
const {searchParams} = new URL(request.url)
|
|
282
|
-
|
|
283
|
-
if (
|
|
284
|
-
!sanity.preview?.session ||
|
|
285
|
-
!searchParams.has('secret') ||
|
|
286
|
-
searchParams.get('secret') !== env.SANITY_PREVIEW_SECRET
|
|
287
|
-
) {
|
|
288
|
-
throw new Response('Invalid secret', {
|
|
289
|
-
status: 401,
|
|
290
|
-
statusText: 'Unauthorized',
|
|
291
|
-
})
|
|
292
|
-
}
|
|
288
|
+
// ./app/routes/resource.preview.ts
|
|
293
289
|
|
|
294
|
-
|
|
290
|
+
import {previewRoute} from 'hydrogen-sanity'
|
|
295
291
|
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
},
|
|
301
|
-
})
|
|
302
|
-
}
|
|
292
|
+
export const {loader} = previewRoute
|
|
293
|
+
|
|
294
|
+
// Optionally, export the supplied action which will disable preview mode when POSTed to
|
|
295
|
+
// export const {action, loader} = previewRoute
|
|
303
296
|
```
|
|
304
297
|
|
|
305
|
-
|
|
298
|
+
### Setup CORS for front-end domains
|
|
299
|
+
|
|
300
|
+
If your Sanity Studio is not embedded in your Hydrogen App, you will need to add a CORS origin to your project for every URL where your app is hosted or running in development.
|
|
301
|
+
|
|
302
|
+
Add `http://localhost:3000` to the CORS origins in your Sanity project settings at [sanity.io/manage](https://sanity.io/manage).
|
|
306
303
|
|
|
307
|
-
|
|
304
|
+
### Modify Content Security Policy for Studio domains
|
|
305
|
+
|
|
306
|
+
You may receive errors in the console due to Content Security Policy (CSP) restrictions due to the default `frame-ancestors` configuration. Modify `entry.server.tsx` to allow any URL that the Studio runs on to display the app in an Iframe.
|
|
308
307
|
|
|
309
308
|
```ts
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
309
|
+
// ./app/entry.server.tsx
|
|
310
|
+
|
|
311
|
+
// Replace this line
|
|
312
|
+
// responseHeaders.set('Content-Security-Policy', header);
|
|
313
|
+
|
|
314
|
+
// With this
|
|
315
|
+
const safeFrameHeader = header.replace(
|
|
316
|
+
'frame-ancestors none',
|
|
317
|
+
'frame-ancestors http://localhost:3333'
|
|
318
|
+
)
|
|
319
|
+
responseHeaders.set('Content-Security-Policy', safeFrameHeader)
|
|
321
320
|
```
|
|
322
321
|
|
|
323
|
-
|
|
322
|
+
### Setup Presentation Tool
|
|
323
|
+
|
|
324
|
+
Now in your Sanity Studio config, import the Presentation tool with the Preview URL set to the preview route you created.
|
|
325
|
+
|
|
326
|
+
```ts
|
|
327
|
+
// ./sanity.config.ts
|
|
328
|
+
|
|
329
|
+
// Add this import
|
|
330
|
+
import {presentationTool} from 'sanity/presentation'
|
|
331
|
+
|
|
332
|
+
export default defineConfig({
|
|
333
|
+
// ...all other settings
|
|
324
334
|
|
|
325
|
-
|
|
335
|
+
plugins: [
|
|
336
|
+
presentationTool({
|
|
337
|
+
previewUrl: {previewMode: {enable: 'http://localhost:3000/resource/preview'}},
|
|
338
|
+
}),
|
|
339
|
+
// ..all other plugins
|
|
340
|
+
],
|
|
341
|
+
})
|
|
342
|
+
```
|
|
326
343
|
|
|
327
|
-
You
|
|
344
|
+
You should now be able to view your Hydrogen app in the Presentation Tool, click to edit any Sanity content and see live updates as you make changes.
|
|
328
345
|
|
|
329
|
-
|
|
346
|
+
## Using `@sanity/client` instead of hydrogen-sanity
|
|
330
347
|
|
|
331
|
-
|
|
348
|
+
For whatever reason, if you choose not to use `hydrogen-sanity` you could still configure `@sanity/react-loader` or `@sanity/client` to get Sanity content into your Hydrogen storefront.
|
|
332
349
|
|
|
333
|
-
|
|
350
|
+
The following example configures Sanity Client.
|
|
334
351
|
|
|
335
352
|
```ts
|
|
336
353
|
// ./server.ts
|
|
@@ -361,18 +378,119 @@ export default {
|
|
|
361
378
|
}
|
|
362
379
|
```
|
|
363
380
|
|
|
364
|
-
Then, in your loaders you'll have access to
|
|
381
|
+
Then, in your loaders and actions you'll have access to Sanity Client in context:
|
|
365
382
|
|
|
366
383
|
```ts
|
|
367
384
|
export async function loader({context, params}: LoaderArgs) {
|
|
368
|
-
const homepage = await context.sanity.fetch(
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
385
|
+
const homepage = await context.sanity.fetch(`*[_type == "page" && _id == $id][0]`, {id: 'home'})
|
|
386
|
+
|
|
387
|
+
return json({homepage})
|
|
388
|
+
}
|
|
389
|
+
```
|
|
390
|
+
|
|
391
|
+
# Migrate to v4 from v3
|
|
392
|
+
|
|
393
|
+
1. Swap `createSanityClient` for `createSanityLoader`
|
|
394
|
+
|
|
395
|
+
The new function will still return a client – useful for mutations when supplied with a write token. But primarily it will now return a configured [Sanity React Loader](https://www.sanity.io/docs/react-loader) which is the new recommendation for performing queries that will take advantage of Visual Editing
|
|
396
|
+
|
|
397
|
+
```diff
|
|
398
|
+
// ./server.ts
|
|
399
|
+
|
|
400
|
+
- import {createSanityClient} from 'hydrogen-sanity';
|
|
401
|
+
+ import {createSanityLoader} from 'hydrogen-sanity';
|
|
402
|
+
```
|
|
403
|
+
|
|
404
|
+
1. Update `query` data fetches to `loadQuery`.
|
|
405
|
+
|
|
406
|
+
The return type of `loadQuery` is different from Sanity Client's `fetch`, with the returned content is inside a `data` attribute. The recommendation for Hydrogen/Remix applications is to name this response `initial` and return it in its entirety in the loader.
|
|
407
|
+
|
|
408
|
+
```diff
|
|
409
|
+
./app/routes/products.$handle.tsx
|
|
410
|
+
|
|
411
|
+
Replace any usage of `query` with `loadQuery`
|
|
412
|
+
Note the different shape for arguments and return value
|
|
413
|
+
- const page = await sanity.query<SanityDocument>({query, params, cache, queryOptions})
|
|
414
|
+
+ const initial = await sanity.loadQuery<SanityDocument>(query, params, {strategy, queryOptions})
|
|
415
|
+
|
|
416
|
+
Replace any Sanity Client fetches
|
|
417
|
+
- const page = await sanity.client.fetch<SanityDocument>(query, params)
|
|
418
|
+
+ const initial = await sanity.loadQuery<SanityDocument>(query, params)
|
|
419
|
+
|
|
420
|
+
- return json({page})
|
|
421
|
+
+ return json({query, params, initial})
|
|
422
|
+
```
|
|
423
|
+
|
|
424
|
+
1. Then in the default export, pass this initial object to the `useQuery` hook imported from React Loader.
|
|
425
|
+
|
|
426
|
+
`useQuery` alone will not rerender the component with preview content. For this, you'll need to add a new component to the root.
|
|
427
|
+
|
|
428
|
+
```tsx
|
|
429
|
+
// ./app/routes/products.$handle.tsx
|
|
372
430
|
|
|
431
|
+
import {useQuery} from '@sanity/react-loader'
|
|
432
|
+
|
|
433
|
+
export default function Route() {
|
|
434
|
+
const {query, params, initial} = useLoaderData()
|
|
435
|
+
const {data, loading} = useQuery(query, params, initial)
|
|
436
|
+
|
|
437
|
+
return loading ? <div>Loading</div> : <Product product={data} />
|
|
438
|
+
}
|
|
439
|
+
```
|
|
440
|
+
|
|
441
|
+
4. Change imports in `root.tsx`
|
|
442
|
+
|
|
443
|
+
The Sanity Visual Editing package exports a ready-made function for Remix to provide live updates to all `useQuery` hooks throughout the application. It is designed to only run when the app is displayed inside an iframe.
|
|
444
|
+
|
|
445
|
+
Update your imports:
|
|
446
|
+
|
|
447
|
+
```diff
|
|
448
|
+
// ./root.tsx
|
|
449
|
+
|
|
450
|
+
- import {PreviewProvider, getPreview} from 'hydrogen-sanity'
|
|
451
|
+
+ import {VisualEditing} from '@sanity/visual-editing/remix'
|
|
452
|
+
```
|
|
453
|
+
|
|
454
|
+
Update root loader and default export to remove the old `PreviewProvider` and replace it with the conditionally imported `VisualEditing` component:
|
|
455
|
+
|
|
456
|
+
```tsx
|
|
457
|
+
// ./root.tsx
|
|
458
|
+
|
|
459
|
+
// Preview config no longer needs to be returned from the Loader
|
|
460
|
+
export async function loader({context}: LoaderArgs) {
|
|
373
461
|
return json({
|
|
374
|
-
|
|
375
|
-
|
|
462
|
+
// ... other loader data
|
|
463
|
+
// Return a boolean for if the app is in preview mode
|
|
464
|
+
preview: !!context.sanity.preview,
|
|
465
|
+
})
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
// Remove the Preview Provider
|
|
469
|
+
export default function App() {
|
|
470
|
+
const {preview, ...data} = useLoaderData<typeof loader>()
|
|
471
|
+
|
|
472
|
+
return (
|
|
473
|
+
<html>
|
|
474
|
+
<head>
|
|
475
|
+
<meta charSet="utf-8" />
|
|
476
|
+
<meta name="viewport" content="width=device-width,initial-scale=1" />
|
|
477
|
+
<Meta />
|
|
478
|
+
<Links />
|
|
479
|
+
</head>
|
|
480
|
+
<body>
|
|
481
|
+
{/* Remove the PreviewProvider wrapper */}
|
|
482
|
+
<PreviewProvider {...preview}>
|
|
483
|
+
<Outlet />
|
|
484
|
+
</Preview>
|
|
485
|
+
{/* Replace with VisualEditing */}
|
|
486
|
+
<Outlet />
|
|
487
|
+
{preview ? <VisualEditing /> : null}
|
|
488
|
+
<ScrollRestoration />
|
|
489
|
+
<Scripts />
|
|
490
|
+
</body>
|
|
491
|
+
</html>
|
|
492
|
+
)
|
|
493
|
+
}
|
|
376
494
|
```
|
|
377
495
|
|
|
378
496
|
## License
|