@sanity/sdk-react 3.0.0-rc.0 → 3.0.0-rc.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 +103 -155
- package/dist/index.d.ts +2488 -2019
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +4435 -1529
- package/dist/index.js.map +1 -1
- package/package.json +55 -59
- package/src/_exports/index.ts +2 -4
- package/src/_exports/sdk-react.ts +30 -18
- package/src/components/SDKProvider.test.tsx +173 -32
- package/src/components/SDKProvider.tsx +87 -28
- package/src/components/SanityApp.test.tsx +68 -12
- package/src/components/SanityApp.tsx +93 -87
- package/src/components/auth/AuthBoundary.recovery.test.tsx +86 -0
- package/src/components/auth/AuthBoundary.test.tsx +7 -7
- package/src/components/auth/AuthBoundary.tsx +29 -6
- package/src/components/auth/DashboardAccessRequest.tsx +37 -0
- package/src/components/auth/LoginError.test.tsx +191 -5
- package/src/components/auth/LoginError.tsx +104 -61
- package/src/components/errors/ChunkLoadError.test.tsx +59 -0
- package/src/components/errors/ChunkLoadError.tsx +56 -0
- package/src/components/errors/CorsErrorComponent.tsx +2 -2
- package/src/components/errors/chunkReloadStorage.ts +57 -0
- package/src/config/handles.ts +29 -27
- package/src/constants.ts +5 -0
- package/src/context/ComlinkTokenRefresh.test.tsx +10 -27
- package/src/context/ComlinkTokenRefresh.tsx +3 -5
- package/src/context/DefaultResourceContext.ts +3 -3
- package/src/context/OrganizationResourcesProvider.test.tsx +189 -0
- package/src/context/OrganizationResourcesProvider.tsx +111 -0
- package/src/context/PerspectiveContext.ts +3 -3
- package/src/context/ProjectContext.ts +15 -0
- package/src/context/ResourceProvider.test.tsx +93 -90
- package/src/context/ResourceProvider.tsx +62 -93
- package/src/context/SDKStudioContext.test.tsx +28 -33
- package/src/context/SanityInstanceProvider.test.tsx +100 -0
- package/src/context/SanityInstanceProvider.tsx +71 -0
- package/src/context/WorkbenchTokenRefresh.test.tsx +106 -0
- package/src/context/WorkbenchTokenRefresh.tsx +61 -0
- package/src/context/renderSanityApp.test.tsx +151 -49
- package/src/context/renderSanityApp.tsx +12 -8
- package/src/context/workbenchToken.ts +63 -0
- package/src/hooks/access/useCheckPermissions.test-d.ts +15 -0
- package/src/hooks/access/useCheckPermissions.test.tsx +53 -0
- package/src/hooks/access/useCheckPermissions.ts +24 -0
- package/src/hooks/agent/agentActions.test.tsx +2 -2
- package/src/hooks/agent/agentActions.ts +52 -72
- package/src/hooks/agent/useAgentResourceContext.ts +1 -1
- package/src/hooks/applications/useApplication.test-d.ts +30 -0
- package/src/hooks/applications/useApplication.ts +22 -0
- package/src/hooks/applications/useApplications.test-d.ts +31 -0
- package/src/hooks/applications/useApplications.ts +25 -0
- package/src/hooks/applications/useDeleteApplication.test-d.ts +12 -0
- package/src/hooks/applications/useDeleteApplication.ts +11 -0
- package/src/hooks/applications/useUpdateApplication.test-d.ts +12 -0
- package/src/hooks/applications/useUpdateApplication.ts +11 -0
- package/src/hooks/auth/useDashboardOrganizationId.test.tsx +2 -8
- package/src/hooks/auth/useVerifyOrgProjects.test.tsx +8 -32
- package/src/hooks/auth/useVerifyOrgProjects.tsx +13 -6
- package/src/hooks/client/useClient.test.tsx +1 -4
- package/src/hooks/client/useClient.ts +1 -0
- package/src/hooks/comlink/useFrameConnection.test.tsx +3 -2
- package/src/hooks/comlink/useFrameConnection.ts +1 -1
- package/src/hooks/comlink/useWindowConnection.test.tsx +4 -3
- package/src/hooks/comlink/useWindowConnection.ts +2 -3
- package/src/hooks/comments/useCommentActions.test.tsx +150 -0
- package/src/hooks/comments/useCommentActions.ts +109 -0
- package/src/hooks/comments/useCommentList.ts +79 -0
- package/src/hooks/comments/useCommentThreads.test.tsx +107 -0
- package/src/hooks/comments/useCommentThreads.ts +73 -0
- package/src/hooks/comments/useComments.test.tsx +242 -0
- package/src/hooks/comments/useComments.ts +59 -0
- package/src/hooks/context/useResource.test.tsx +32 -0
- package/src/hooks/context/useResource.ts +24 -0
- package/src/hooks/context/useSanityInstance.test.tsx +18 -1
- package/src/hooks/context/useSanityInstance.ts +6 -6
- package/src/hooks/dashboard/useManageFavorite.test.tsx +110 -46
- package/src/hooks/dashboard/useManageFavorite.ts +11 -17
- package/src/hooks/dashboard/useNavigateToStudioDocument.test.ts +5 -7
- package/src/hooks/dashboard/useNavigateToStudioDocument.ts +2 -6
- package/src/hooks/dashboard/useRecordDocumentHistoryEvent.test.ts +0 -2
- package/src/hooks/dashboard/useRecordDocumentHistoryEvent.ts +2 -2
- package/src/hooks/dashboard/useWindowTitle.test.ts +244 -0
- package/src/hooks/dashboard/useWindowTitle.ts +114 -0
- package/src/hooks/datasets/useDatasets.test.tsx +116 -0
- package/src/hooks/datasets/useDatasets.ts +33 -13
- package/src/hooks/document/useApplyDocumentActions.test.tsx +48 -0
- package/src/hooks/document/useApplyDocumentActions.ts +66 -48
- package/src/hooks/document/useCreateDocument.test.tsx +83 -0
- package/src/hooks/document/useCreateDocument.ts +117 -0
- package/src/hooks/document/useDocument.test.tsx +37 -8
- package/src/hooks/document/useDocument.ts +131 -66
- package/src/hooks/document/useDocumentEvent.test.tsx +16 -4
- package/src/hooks/document/useDocumentEvent.ts +16 -10
- package/src/hooks/document/useDocumentPermissions.test.tsx +8 -7
- package/src/hooks/document/useDocumentPermissions.ts +71 -39
- package/src/hooks/document/useDocumentSyncStatus.test.ts +1 -0
- package/src/hooks/document/useDocumentSyncStatus.ts +13 -9
- package/src/hooks/document/useEditDocument.test.tsx +21 -14
- package/src/hooks/document/useEditDocument.ts +163 -17
- package/src/hooks/documents/useDocuments.test.tsx +64 -44
- package/src/hooks/documents/useDocuments.ts +31 -20
- package/src/hooks/helpers/createCallbackHook.test.tsx +13 -19
- package/src/hooks/helpers/createFetcherHook.test.tsx +180 -0
- package/src/hooks/helpers/createFetcherHook.ts +69 -0
- package/src/hooks/helpers/createMutationHook.test.tsx +125 -0
- package/src/hooks/helpers/createMutationHook.tsx +93 -0
- package/src/hooks/helpers/createStateSourceHook.test.tsx +10 -10
- package/src/hooks/helpers/createStateSourceHook.tsx +2 -1
- package/src/hooks/{document/useApplyDocumentActions.test.ts → helpers/useApplyActions.test.tsx} +92 -53
- package/src/hooks/helpers/useApplyActions.ts +68 -0
- package/src/hooks/helpers/useNormalizedResourceOptions.test.tsx +226 -0
- package/src/hooks/helpers/useNormalizedResourceOptions.ts +79 -48
- package/src/hooks/helpers/useResolvedProjectId.test.tsx +59 -0
- package/src/hooks/helpers/useResolvedProjectId.ts +35 -0
- package/src/hooks/helpers/useTrackHookUsage.ts +37 -0
- package/src/hooks/installations/useInstallation.test-d.ts +19 -0
- package/src/hooks/installations/useInstallation.ts +22 -0
- package/src/hooks/installations/useInstallations.test-d.ts +26 -0
- package/src/hooks/installations/useInstallations.ts +25 -0
- package/src/hooks/organizations/useOrganization.test-d.ts +60 -0
- package/src/hooks/organizations/useOrganization.test.ts +63 -0
- package/src/hooks/organizations/useOrganization.ts +34 -0
- package/src/hooks/organizations/useOrganizations.test-d.ts +68 -0
- package/src/hooks/organizations/useOrganizations.test.ts +63 -0
- package/src/hooks/organizations/useOrganizations.ts +39 -0
- package/src/hooks/paginatedDocuments/usePaginatedDocuments.test.tsx +34 -27
- package/src/hooks/paginatedDocuments/usePaginatedDocuments.ts +35 -22
- package/src/hooks/presence/usePresence.test.tsx +0 -15
- package/src/hooks/presence/usePresence.ts +22 -20
- package/src/hooks/presence/usePresenceForDocument.test.tsx +141 -0
- package/src/hooks/presence/usePresenceForDocument.ts +104 -0
- package/src/hooks/presence/useReportPresence.test.tsx +202 -0
- package/src/hooks/presence/useReportPresence.ts +170 -0
- package/src/hooks/preview/useDocumentPreview.test.tsx +0 -1
- package/src/hooks/preview/useDocumentPreview.tsx +12 -14
- package/src/hooks/projection/useDocumentProjection.test.tsx +78 -9
- package/src/hooks/projection/useDocumentProjection.ts +81 -2
- package/src/hooks/projects/useProject.test-d.ts +56 -0
- package/src/hooks/projects/useProject.test.tsx +120 -0
- package/src/hooks/projects/useProject.ts +42 -43
- package/src/hooks/projects/useProjects.test-d.ts +60 -0
- package/src/hooks/projects/useProjects.test.ts +45 -98
- package/src/hooks/projects/useProjects.ts +17 -32
- package/src/hooks/query/useQuery.test.tsx +6 -5
- package/src/hooks/query/useQuery.ts +94 -17
- package/src/hooks/releases/useActiveReleases.test.tsx +13 -7
- package/src/hooks/releases/useActiveReleases.ts +28 -24
- package/src/hooks/releases/useAllReleases.test.tsx +93 -0
- package/src/hooks/releases/useAllReleases.ts +62 -0
- package/src/hooks/releases/useApplyReleaseActions.test.tsx +66 -0
- package/src/hooks/releases/useApplyReleaseActions.ts +82 -0
- package/src/hooks/releases/usePerspective.test.tsx +18 -10
- package/src/hooks/releases/usePerspective.ts +13 -17
- package/src/hooks/users/useUser.test.tsx +3 -9
- package/src/hooks/users/useUser.ts +4 -8
- package/src/hooks/users/useUsers.test.tsx +103 -7
- package/src/hooks/users/useUsers.ts +38 -5
- package/src/utils/resolveOrgResources.test.ts +111 -0
- package/src/utils/resolveOrgResources.ts +69 -0
- package/src/hooks/context/useDefaultResource.test.tsx +0 -25
- package/src/hooks/context/useDefaultResource.ts +0 -30
- package/src/hooks/dashboard/useDispatchIntent.test.ts +0 -254
- package/src/hooks/dashboard/useDispatchIntent.ts +0 -157
- package/src/hooks/dashboard/utils/useResourceIdFromDocumentHandle.test.ts +0 -107
- package/src/hooks/dashboard/utils/useResourceIdFromDocumentHandle.ts +0 -39
- package/src/hooks/datasets/useDatasets.test.ts +0 -66
- package/src/hooks/helpers/useNormalizedResourceOptions.test.ts +0 -65
- package/src/hooks/projects/useProject.test.ts +0 -79
package/README.md
CHANGED
|
@@ -5,9 +5,7 @@
|
|
|
5
5
|
<h1 align="center">Sanity App SDK (React)</h1>
|
|
6
6
|
</p>
|
|
7
7
|
|
|
8
|
-
React hooks for creating Sanity applications. Live by default, optimistic updates, multi-
|
|
9
|
-
|
|
10
|
-
> **Requires React 19** — `react` and `react-dom` `^19.2.0` are the minimum peer dependencies.
|
|
8
|
+
React hooks for creating Sanity applications. Live by default, optimistic updates, multi-project support.
|
|
11
9
|
|
|
12
10
|
---
|
|
13
11
|
|
|
@@ -32,31 +30,22 @@ Opens at `https://www.sanity.io/welcome?dev=http%3A%2F%2Flocalhost%3A3333`, prox
|
|
|
32
30
|
### 2. Project configuration
|
|
33
31
|
|
|
34
32
|
```tsx
|
|
35
|
-
import {SanityApp} from '@sanity/sdk-react'
|
|
33
|
+
import {SanityApp, type SanityConfig} from '@sanity/sdk-react'
|
|
34
|
+
|
|
35
|
+
const config: SanityConfig[] = [
|
|
36
|
+
{projectId: 'abc123', dataset: 'production'},
|
|
37
|
+
{projectId: 'def456', dataset: 'production'}, // multi-project support
|
|
38
|
+
]
|
|
36
39
|
|
|
37
40
|
export function App() {
|
|
38
41
|
return (
|
|
39
|
-
<SanityApp
|
|
40
|
-
resources={{
|
|
41
|
-
'default': {projectId: 'abc123', dataset: 'production'},
|
|
42
|
-
'second-project': {projectId: 'def456', dataset: 'production'},
|
|
43
|
-
}}
|
|
44
|
-
fallback={<div>Loading...</div>}
|
|
45
|
-
>
|
|
42
|
+
<SanityApp config={config} fallback={<div>Loading...</div>}>
|
|
46
43
|
<YourApp />
|
|
47
44
|
</SanityApp>
|
|
48
45
|
)
|
|
49
46
|
}
|
|
50
47
|
```
|
|
51
48
|
|
|
52
|
-
In Sanity, a **resource** identifies where your data lives. It can be one of:
|
|
53
|
-
|
|
54
|
-
- a project and dataset pair (`{ projectId, dataset }`)
|
|
55
|
-
- a media library (`{ mediaLibraryId }`)
|
|
56
|
-
- or a canvas (`{ canvasId }`)
|
|
57
|
-
|
|
58
|
-
The `resources` prop is a map of named resources. Each resource tells the SDK where to read and write data. The resource keyed `"default"` is used automatically when no explicit resource is specified in a hook.
|
|
59
|
-
|
|
60
49
|
**Auth is automatic** — Dashboard injects an auth token via iframe. No custom login flow is needed for your application.
|
|
61
50
|
|
|
62
51
|
---
|
|
@@ -71,14 +60,11 @@ Document handles are a core concept for apps built with the App SDK. Document ha
|
|
|
71
60
|
type DocumentHandle = {
|
|
72
61
|
documentId: string
|
|
73
62
|
documentType: string
|
|
74
|
-
|
|
63
|
+
projectId?: string // optional if using the default projectId or inside a ResourceProvider
|
|
64
|
+
dataset?: string // optional if using the default dataset or inside a ResourceProvider
|
|
75
65
|
}
|
|
76
66
|
```
|
|
77
67
|
|
|
78
|
-
The `resource` field identifies where the document lives. When you fetch document handles from hooks like `useDocuments`, the `resource` is automatically populated from the current context.
|
|
79
|
-
|
|
80
|
-
Most hooks also accept `resourceName` to target a specific named resource declared in the `resources` prop on `<SanityApp>`, or a `resource` object directly. When neither is provided, the `"default"` resource is used.
|
|
81
|
-
|
|
82
68
|
**Best practice:** Fetch document handles first → pass them to child components → fetch individual document content from child components.
|
|
83
69
|
|
|
84
70
|
---
|
|
@@ -122,6 +108,10 @@ const {data} = useQuery({
|
|
|
122
108
|
#### Document Manipulation
|
|
123
109
|
|
|
124
110
|
```tsx
|
|
111
|
+
// Create a new document and get back its handle
|
|
112
|
+
const createArticle = useCreateDocument({documentType: 'article'})
|
|
113
|
+
const newHandle = await createArticle({title: 'Untitled', status: 'draft'})
|
|
114
|
+
|
|
125
115
|
// Edit field (emits optimistic updates to useEditDocument listeners, creates a draft automatically)
|
|
126
116
|
const editTitle = useEditDocument({...handle, path: 'title'})
|
|
127
117
|
editTitle('New Title') // fires on every keystroke, debounced internally
|
|
@@ -137,7 +127,7 @@ import {
|
|
|
137
127
|
unpublishDocument,
|
|
138
128
|
deleteDocument,
|
|
139
129
|
createDocument,
|
|
140
|
-
|
|
130
|
+
discardDraft,
|
|
141
131
|
} from '@sanity/sdk-react'
|
|
142
132
|
|
|
143
133
|
const apply = useApplyDocumentActions()
|
|
@@ -147,13 +137,6 @@ await apply(publishDocument(handle))
|
|
|
147
137
|
|
|
148
138
|
// Batch actions
|
|
149
139
|
await apply([publishDocument(handle1), publishDocument(handle2), deleteDocument(handle3)])
|
|
150
|
-
|
|
151
|
-
// Create new document with an optional initial content
|
|
152
|
-
const newHandle = createDocumentHandle({
|
|
153
|
-
documentId: crypto.randomUUID(),
|
|
154
|
-
documentType: 'article',
|
|
155
|
-
})
|
|
156
|
-
await apply(createDocument(newHandle, {title: 'Untitled', status: 'draft'}))
|
|
157
140
|
```
|
|
158
141
|
|
|
159
142
|
#### Events & Permissions
|
|
@@ -163,7 +146,7 @@ await apply(createDocument(newHandle, {title: 'Untitled', status: 'draft'}))
|
|
|
163
146
|
useDocumentEvent({
|
|
164
147
|
...handle,
|
|
165
148
|
onEvent: (event) => {
|
|
166
|
-
// event.type: '
|
|
149
|
+
// event.type: 'edited' | 'published' | 'deleted' | 'created' | 'unpublished' | 'discarded' | ...
|
|
167
150
|
console.log(event.type, event.documentId)
|
|
168
151
|
},
|
|
169
152
|
})
|
|
@@ -193,45 +176,37 @@ The `useApplyDocumentActions` hook is used to perform document lifecycle operati
|
|
|
193
176
|
| `publishDocument` | Publish a draft (copy draft → published) |
|
|
194
177
|
| `unpublishDocument` | Unpublish (delete published, keep draft) |
|
|
195
178
|
| `deleteDocument` | Delete document entirely (draft and published) |
|
|
196
|
-
| `
|
|
179
|
+
| `discardDraft` | Discard draft changes, revert to published |
|
|
197
180
|
|
|
198
181
|
#### Creating Documents
|
|
199
182
|
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
1. Generate your own document ID (using `crypto.randomUUID()`)
|
|
203
|
-
2. Create a document handle with `createDocumentHandle`
|
|
204
|
-
3. Apply the `createDocument` action using the document handle, along with optional initial content
|
|
183
|
+
`useCreateDocument` handles the common case: it generates the document ID for you and returns the new document's handle, ready to pass to `useDocument`, `useEditDocument`, or your router.
|
|
205
184
|
|
|
206
185
|
```tsx
|
|
207
|
-
import {
|
|
186
|
+
import {useCreateDocument} from '@sanity/sdk-react'
|
|
208
187
|
|
|
209
188
|
function CreateArticleButton() {
|
|
210
|
-
const
|
|
189
|
+
const createArticle = useCreateDocument({documentType: 'article'})
|
|
211
190
|
|
|
212
|
-
const handleCreateArticle = () => {
|
|
213
|
-
const
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
191
|
+
const handleCreateArticle = async () => {
|
|
192
|
+
const handle = await createArticle({
|
|
193
|
+
title: 'New Article',
|
|
194
|
+
status: 'draft',
|
|
195
|
+
author: {_type: 'reference', _ref: 'author-123'},
|
|
217
196
|
})
|
|
218
197
|
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
title: 'New Article',
|
|
222
|
-
status: 'draft',
|
|
223
|
-
author: {_type: 'reference', _ref: 'author-123'},
|
|
224
|
-
}),
|
|
225
|
-
)
|
|
226
|
-
|
|
227
|
-
// Navigate to the new document
|
|
228
|
-
navigate(`/articles/${newId}`)
|
|
198
|
+
// Navigate to the new document using the returned handle
|
|
199
|
+
navigate(`/articles/${handle.documentId}`)
|
|
229
200
|
}
|
|
230
201
|
|
|
231
202
|
return <button onClick={handleCreateArticle}>Create Article</button>
|
|
232
203
|
}
|
|
233
204
|
```
|
|
234
205
|
|
|
206
|
+
To use a specific ID instead of a generated one, pass it on the handle (`useCreateDocument({documentType: 'article', documentId})`) or per call (`createArticle(initialValue, {documentId})`).
|
|
207
|
+
|
|
208
|
+
For atomic create-and-publish, or to create several documents in a single transaction, drop down to `useApplyDocumentActions` with the `createDocument` and `publishDocument` action creators (see [Batch Operations](#batch-operations) below).
|
|
209
|
+
|
|
235
210
|
#### Publishing Documents
|
|
236
211
|
|
|
237
212
|
```tsx
|
|
@@ -322,6 +297,27 @@ function ArticleList() {
|
|
|
322
297
|
|
|
323
298
|
---
|
|
324
299
|
|
|
300
|
+
### Viewport-Based Lazy Loading
|
|
301
|
+
|
|
302
|
+
Pass a `ref` to `useDocumentProjection` to fetch only when the element enters the viewport. Wrap the component in Suspense like any other data hook:
|
|
303
|
+
|
|
304
|
+
```tsx
|
|
305
|
+
import {useRef} from 'react'
|
|
306
|
+
|
|
307
|
+
function LazyArticleCard({handle}: {handle: DocumentHandle}) {
|
|
308
|
+
const ref = useRef<HTMLDivElement>(null)
|
|
309
|
+
const {data} = useDocumentProjection({
|
|
310
|
+
...handle,
|
|
311
|
+
ref, // Only fetches when element is visible
|
|
312
|
+
projection: '{ title }',
|
|
313
|
+
})
|
|
314
|
+
|
|
315
|
+
return <div ref={ref}>{data?.title}</div>
|
|
316
|
+
}
|
|
317
|
+
```
|
|
318
|
+
|
|
319
|
+
---
|
|
320
|
+
|
|
325
321
|
### Draft/Published Model
|
|
326
322
|
|
|
327
323
|
Sanity has two document states:
|
|
@@ -334,7 +330,7 @@ The SDK handles updating the document state automatically:
|
|
|
334
330
|
- `useDocument()` returns draft if exists, else published
|
|
335
331
|
- `useEditDocument()` creates draft on first edit (automatic)
|
|
336
332
|
- `publishDocument()` copies draft → published, deletes draft
|
|
337
|
-
- `
|
|
333
|
+
- `discardDraft()` deletes draft, reverts to published
|
|
338
334
|
|
|
339
335
|
#### LiveEdit Documents
|
|
340
336
|
|
|
@@ -344,7 +340,6 @@ For documents that don't need the draft/published workflow (such as settings, co
|
|
|
344
340
|
const settingsHandle: DocumentHandle = {
|
|
345
341
|
documentId: 'site-settings',
|
|
346
342
|
documentType: 'settings',
|
|
347
|
-
resource: {projectId: 'abc123', dataset: 'production'},
|
|
348
343
|
liveEdit: true, // Edits apply directly without creating a draft
|
|
349
344
|
}
|
|
350
345
|
|
|
@@ -356,7 +351,7 @@ const editSettings = useEditDocument(settingsHandle)
|
|
|
356
351
|
|
|
357
352
|
- Drafts will not be created when the document is edited
|
|
358
353
|
- Edits will be applied directly to the published document
|
|
359
|
-
- `publishDocument()`, `unpublishDocument()`, and `
|
|
354
|
+
- `publishDocument()`, `unpublishDocument()`, and `discardDraft()` actions cannot be used (since liveEdit documents are always published and do not have drafts)
|
|
360
355
|
|
|
361
356
|
For more details, see the [Sanity documentation on liveEdit documents](https://www.sanity.io/docs/content-lake/drafts).
|
|
362
357
|
|
|
@@ -376,80 +371,32 @@ Any mutation to a subscribed document (even fields you don't display) will trigg
|
|
|
376
371
|
|
|
377
372
|
---
|
|
378
373
|
|
|
379
|
-
### Multi-
|
|
374
|
+
### Multi-Project Access
|
|
380
375
|
|
|
381
|
-
|
|
376
|
+
The SDK supports accessing documents from multiple projects and datasets simultaneously. There are two main approaches:
|
|
382
377
|
|
|
383
|
-
#### Approach 1:
|
|
378
|
+
#### Approach 1: Specify Project/Dataset Directly in the Handle
|
|
384
379
|
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
```tsx
|
|
388
|
-
import {SanityApp} from '@sanity/sdk-react'
|
|
389
|
-
|
|
390
|
-
export function App() {
|
|
391
|
-
return (
|
|
392
|
-
<SanityApp
|
|
393
|
-
resources={{
|
|
394
|
-
default: {projectId: 'project-a', dataset: 'production'},
|
|
395
|
-
staging: {projectId: 'project-a', dataset: 'staging'},
|
|
396
|
-
media: {mediaLibraryId: 'my-media-library'},
|
|
397
|
-
}}
|
|
398
|
-
fallback={<div>Loading...</div>}
|
|
399
|
-
>
|
|
400
|
-
<MyApp />
|
|
401
|
-
</SanityApp>
|
|
402
|
-
)
|
|
403
|
-
}
|
|
404
|
-
```
|
|
405
|
-
|
|
406
|
-
Then reference them in hooks:
|
|
407
|
-
|
|
408
|
-
```tsx
|
|
409
|
-
import {useDocument, useQuery} from '@sanity/sdk-react'
|
|
410
|
-
|
|
411
|
-
function StagingPreview({documentId}: {documentId: string}) {
|
|
412
|
-
const {data} = useDocument({
|
|
413
|
-
documentId,
|
|
414
|
-
documentType: 'article',
|
|
415
|
-
resourceName: 'staging',
|
|
416
|
-
})
|
|
417
|
-
return <pre>{JSON.stringify(data, null, 2)}</pre>
|
|
418
|
-
}
|
|
419
|
-
|
|
420
|
-
function MediaAssets() {
|
|
421
|
-
const {data} = useQuery({
|
|
422
|
-
query: '*[_type == "sanity.asset"][0...10]',
|
|
423
|
-
resourceName: 'media',
|
|
424
|
-
})
|
|
425
|
-
return (
|
|
426
|
-
<ul>
|
|
427
|
-
{data?.map((asset) => (
|
|
428
|
-
<li key={asset._id}>{asset.originalFilename}</li>
|
|
429
|
-
))}
|
|
430
|
-
</ul>
|
|
431
|
-
)
|
|
432
|
-
}
|
|
433
|
-
```
|
|
434
|
-
|
|
435
|
-
#### Approach 2: Pass a Resource Object Directly
|
|
436
|
-
|
|
437
|
-
If you only need to access a resource in one or two places within your app, you can pass it inline via the `resource` option:
|
|
380
|
+
Pass `projectId` and `dataset` directly in document handles to fetch data from specific projects (note that any `projectId` and `dataset` pair you pass must be defined in your application’s array of [SanityConfig objects](https://www.sanity.io/docs/app-sdk/sdk-configuration#d95b8773097c)):
|
|
438
381
|
|
|
439
382
|
```tsx
|
|
440
383
|
import {useDocument} from '@sanity/sdk-react'
|
|
441
384
|
|
|
442
385
|
function MultiProjectComponent() {
|
|
386
|
+
// Fetch from Project A
|
|
443
387
|
const {data: productA} = useDocument({
|
|
444
388
|
documentId: 'product-123',
|
|
445
389
|
documentType: 'product',
|
|
446
|
-
|
|
390
|
+
projectId: 'project-a',
|
|
391
|
+
dataset: 'production',
|
|
447
392
|
})
|
|
448
393
|
|
|
394
|
+
// Fetch from Project B
|
|
449
395
|
const {data: productB} = useDocument({
|
|
450
396
|
documentId: 'product-456',
|
|
451
397
|
documentType: 'product',
|
|
452
|
-
|
|
398
|
+
projectId: 'project-b',
|
|
399
|
+
dataset: 'staging',
|
|
453
400
|
})
|
|
454
401
|
|
|
455
402
|
return (
|
|
@@ -461,41 +408,46 @@ function MultiProjectComponent() {
|
|
|
461
408
|
}
|
|
462
409
|
```
|
|
463
410
|
|
|
464
|
-
#### Approach
|
|
411
|
+
#### Approach 2: Use ResourceProvider to Set Context
|
|
465
412
|
|
|
466
|
-
|
|
413
|
+
Wrap components in `ResourceProvider` to set default project/dataset values for all child components:
|
|
467
414
|
|
|
468
415
|
```tsx
|
|
469
|
-
|
|
416
|
+
// App.tsx
|
|
417
|
+
import {ResourceProvider, useDocument, useSanityInstance} from '@sanity/sdk-react'
|
|
470
418
|
|
|
471
419
|
function ProductCard({productId}: {productId: string}) {
|
|
472
|
-
//
|
|
420
|
+
// Get the current project/dataset from context
|
|
421
|
+
const {config} = useSanityInstance()
|
|
422
|
+
|
|
423
|
+
// No need to specify projectId/dataset - inherited from ResourceProvider
|
|
473
424
|
const {data: product} = useDocument({
|
|
474
425
|
documentId: productId,
|
|
475
426
|
documentType: 'product',
|
|
476
427
|
})
|
|
477
428
|
|
|
478
|
-
return
|
|
429
|
+
return (
|
|
430
|
+
<div>
|
|
431
|
+
<h3>{product?.title}</h3>
|
|
432
|
+
<p>
|
|
433
|
+
From: {config.projectId}.{config.dataset}
|
|
434
|
+
</p>
|
|
435
|
+
</div>
|
|
436
|
+
)
|
|
479
437
|
}
|
|
480
438
|
|
|
481
439
|
export function MultiProjectApp() {
|
|
482
440
|
return (
|
|
483
441
|
<div>
|
|
484
442
|
{/* Products from Project A */}
|
|
485
|
-
<ResourceProvider
|
|
486
|
-
resource={{projectId: 'project-a', dataset: 'production'}}
|
|
487
|
-
fallback={<div>Loading...</div>}
|
|
488
|
-
>
|
|
443
|
+
<ResourceProvider projectId="project-a" dataset="production" fallback={<div>Loading...</div>}>
|
|
489
444
|
<h2>Project A Products</h2>
|
|
490
445
|
<ProductCard productId="product-123" />
|
|
491
446
|
<ProductCard productId="product-456" />
|
|
492
447
|
</ResourceProvider>
|
|
493
448
|
|
|
494
449
|
{/* Products from Project B */}
|
|
495
|
-
<ResourceProvider
|
|
496
|
-
resource={{projectId: 'project-b', dataset: 'staging'}}
|
|
497
|
-
fallback={<div>Loading...</div>}
|
|
498
|
-
>
|
|
450
|
+
<ResourceProvider projectId="project-b" dataset="staging" fallback={<div>Loading...</div>}>
|
|
499
451
|
<h2>Project B Products</h2>
|
|
500
452
|
<ProductCard productId="product-789" />
|
|
501
453
|
</ResourceProvider>
|
|
@@ -506,16 +458,17 @@ export function MultiProjectApp() {
|
|
|
506
458
|
|
|
507
459
|
**Key Points:**
|
|
508
460
|
|
|
509
|
-
-
|
|
510
|
-
-
|
|
511
|
-
- `
|
|
512
|
-
- You can nest
|
|
461
|
+
- When using hooks that take document handles as arguments (such useDocument, useEditDocument, useQuery, etc.), the document handles’ `projectId` and `dataset` values can be explicitly set to fetch documents from arbitrary projects and datasets
|
|
462
|
+
- The ResourceProvider component is used to create a project ID and dataset context that child components will inherit from; this can negate the need to specify the project ID and dataset values for document handles in hooks called by child components
|
|
463
|
+
- Use `useSanityInstance()` to access the context configuration for the current component: `const {config} = useSanityInstance()`
|
|
464
|
+
- You can nest ResourceProvider components to create component trees with different project/dataset configurations — but be aware that, when the project ID and dataset values for document handles are _not_ specified, the project ID and dataset from the closest ResourceProvider context will be used
|
|
465
|
+
- Regardless of the approach you use, the project IDs and dataset names you reference (whether in document handles or ResourceProviders) must be enumerated in your application’s [SanityConfig objects](https://www.sanity.io/docs/app-sdk/sdk-configuration#d95b8773097c)
|
|
513
466
|
|
|
514
467
|
---
|
|
515
468
|
|
|
516
469
|
### Using the SDK inside Sanity Studio
|
|
517
470
|
|
|
518
|
-
The SDK can be embedded directly inside a Sanity Studio with zero manual configuration. Sanity Studio provides `SDKStudioContext` automatically, so `SanityApp` derives
|
|
471
|
+
The SDK can be embedded directly inside a Sanity Studio with zero manual configuration. Sanity Studio provides `SDKStudioContext` automatically, so `SanityApp` derives `projectId`, `dataset`, and auth from the Studio's workspace without any setup.
|
|
519
472
|
|
|
520
473
|
#### Zero-config setup (recommended)
|
|
521
474
|
|
|
@@ -549,13 +502,11 @@ function StudioSDKWrapper({children}) {
|
|
|
549
502
|
|
|
550
503
|
#### Explicit config takes precedence
|
|
551
504
|
|
|
552
|
-
If you pass
|
|
505
|
+
If you pass a `config` prop to `SanityApp`, this config will take precedence over any workspace config picked up by `SDKStudioContext`:
|
|
553
506
|
|
|
554
507
|
```tsx
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
fallback={<Loading />}
|
|
558
|
-
>
|
|
508
|
+
// This uses the explicit config, not the Studio workspace
|
|
509
|
+
<SanityApp config={{projectId: 'other-project', dataset: 'staging'}} fallback={<Loading />}>
|
|
559
510
|
<MyComponent />
|
|
560
511
|
</SanityApp>
|
|
561
512
|
```
|
|
@@ -566,35 +517,32 @@ If the Studio provides a reactive token source via `workspace.auth.token`, the S
|
|
|
566
517
|
|
|
567
518
|
For older Studios that don't expose a token source, the SDK falls back to discovering the auth token from `localStorage` or cookie auth.
|
|
568
519
|
|
|
569
|
-
#### `studioMode`
|
|
520
|
+
#### Migrating from `studioMode`
|
|
570
521
|
|
|
571
|
-
The `studioMode` config field
|
|
522
|
+
The `studioMode` config field is deprecated. If you are currently using it, the recommended replacement is to use the zero-config `SDKStudioContext` approach described above — which requires no `config` prop at all.
|
|
572
523
|
|
|
573
|
-
If you
|
|
524
|
+
If you need to pass an explicit config, replace `studioMode` with `studio`:
|
|
574
525
|
|
|
575
526
|
```diff
|
|
576
527
|
const config: SanityConfig = {
|
|
577
|
-
|
|
578
|
-
|
|
528
|
+
projectId: 'my-project',
|
|
529
|
+
dataset: 'production',
|
|
579
530
|
- studioMode: { enabled: true },
|
|
580
531
|
+ studio: {},
|
|
581
532
|
}
|
|
582
533
|
```
|
|
583
534
|
|
|
584
|
-
See the [Migration Guide](./guides/0-Migration-Guide.md) for all v3 breaking changes and upgrade steps.
|
|
585
|
-
|
|
586
535
|
---
|
|
587
536
|
|
|
588
|
-
### TypeScript
|
|
537
|
+
### TypeScript & TypeGen
|
|
589
538
|
|
|
590
|
-
```
|
|
591
|
-
|
|
539
|
+
```bash
|
|
540
|
+
# Generate types from your schema
|
|
541
|
+
npx sanity typegen generate
|
|
542
|
+
```
|
|
592
543
|
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
title: string
|
|
596
|
-
body: string
|
|
597
|
-
}
|
|
544
|
+
```tsx
|
|
545
|
+
import type {Article} from './sanity.types'
|
|
598
546
|
|
|
599
547
|
const {data} = useDocument<Article>(handle)
|
|
600
548
|
// data is typed as Article
|