diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 0244819de4..1814ee1cd2 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -44,6 +44,9 @@ jobs:
- run: npm ci
- run: npm run lint
if: github.event.inputs.test != 'false'
+ # @TODO find out how to make this command work on the CI
+ #- run: npm run update:icons
+ # if: github.event.inputs.test != 'false'
- run: npm run prepublishOnly
- run: npm run test:node-esm-cjs
name: Test if Node.js ESM native modes works, without breaking CJS modes
diff --git a/README.md b/README.md
index 8cfeb30cc4..3a3862540d 100644
--- a/README.md
+++ b/README.md
@@ -25,11 +25,17 @@
- [Limits](#limits)
- [`next-sanity/studio` (dev-preview)](#next-sanitystudio-dev-preview)
- [Usage](#usage)
+ - [Next 13 `app/studio`](#next-13-appstudio)
+ - [Next 12 or `pages/studio`](#next-12-or-pagesstudio)
- [Opt-in to using `StudioProvider` and `StudioLayout`](#opt-in-to-using-studioprovider-and-studiolayout)
- - [Customize ` `](#customize-serverstylesheetdocument-)
- - [Full-control mode](#full-control-mode)
- [`next-sanity/webhook`](#next-sanitywebhook)
- [Migrate](#migrate)
+ - [From `v2`](#from-v2)
+ - [`NextStudioGlobalStyle` is removed](#nextstudioglobalstyle-is-removed)
+ - [`ServerStyleSheetDocument` is removed](#serverstylesheetdocument-is-removed)
+ - [The internal `isWorkspaceWithTheme` and `isWorkspaces` utils are no longer exported](#the-internal-isworkspacewiththeme-and-isworkspaces-utils-are-no-longer-exported)
+ - [The `useBackgroundColorsFromTheme`, `useBasePath`, `useConfigWithBasePath`, and `useTextFontFamilyFromTheme`, hooks are removed](#the-usebackgroundcolorsfromtheme-usebasepath-useconfigwithbasepath-and-usetextfontfamilyfromtheme-hooks-are-removed)
+ - [The `NextStudioHead` component has moved from `next-sanity/studio` to `next-sanity/studio/head`](#the-nextstudiohead-component-has-moved-from-next-sanitystudio-to-next-sanitystudiohead)
- [From `v1`](#from-v1)
- [`createPreviewSubscriptionHook` is replaced with `definePreview`](#createpreviewsubscriptionhook-is-replaced-with-definepreview)
- [Before](#before)
@@ -450,149 +456,142 @@ The latest version of Sanity Studio allows you to embed a near-infinitely config
### Usage
-The basic setup is two files:
+The basic setup is 2 components, `NextStudio` and `NextStudioHead`.
+`NextStudio` loads up the `import {Studio} from 'sanity'` component for you and wraps it in a Next-friendly layout.
+While `NextStudioHead` sets necessary `
` meta tags such as ` ` to ensure the responsive CSS in the Studio works as expected.
-1. `pages/[[...index]].tsx`
+Both the Next 13 and 12 examples uses this config file:
+`sanity.config.ts`:
-```tsx
-// Import your sanity.config.ts file
-import config from '../sanity.config'
-import {NextStudio} from 'next-sanity/studio'
+```ts
+import {visionTool} from '@sanity/vision'
+import {defineConfig} from 'sanity'
+import {deskTool} from 'sanity/desk'
-export default function StudioPage() {
- // Loads the Studio, with all the needed meta tags and global CSS required for it to render correctly
- return
-}
-```
+import {schemaTypes} from './schemas'
-The ` ` wraps ` ` component and supports forwarding all its props:
+const projectId = process.env.NEXT_PUBLIC_SANITY_PROJECT_ID!
+const dataset = process.env.NEXT_PUBLIC_SANITY_DATASET!
-```tsx
-import {Studio} from 'sanity'
+export default defineConfig({
+ basePath: '/studio', // <-- important that `basePath` matches the route you're mounting your studio from, it applies to both `/pages` and `/app`
+
+ projectId,
+ dataset,
+
+ plugins: [deskTool(), visionTool()],
+
+ schema: {
+ types: schemaTypes,
+ },
+})
```
-2. `pages/_document.tsx`
+To use `sanity.cli.ts` with the same `projectId` and `dataset` as your `sanity.config.ts`:
-```tsx
-import {ServerStyleSheetDocument} from 'next-sanity/studio'
+```ts
+/* eslint-disable no-process-env */
+import {loadEnvConfig} from '@next/env'
+import {defineCliConfig} from 'sanity/cli'
+
+const dev = process.env.NODE_ENV !== 'production'
+loadEnvConfig(__dirname, dev, {info: () => null, error: console.error})
-// Set up SSR for styled-components, ensuring there's no missing CSS when deploying a Studio in Next.js into production
-export default class Document extends ServerStyleSheetDocument {}
+const projectId = process.env.NEXT_PUBLIC_SANITY_PROJECT_ID
+const dataset = process.env.NEXT_PUBLIC_SANITY_DATASET
+
+export default defineCliConfig({api: {projectId, dataset}})
```
-### Opt-in to using `StudioProvider` and `StudioLayout`
+Now you can run commands like `npx sanity cors add`. See `npx sanity help` for a full list of what you can do.
-If you want to go lower level and have more control over the studio you can pass `StudioProvider` and `StudioLayout` from `sanity` as `children`:
+#### Next 13 `app/studio`
+
+In Next 13's `appDir` mode you use `page.tsx` to load `NextStudio`, and optionally (recommended, especially if you want great support for iPhones and other devices with display cutouts like "The Notch" or "Dynamic Island") export `NextStudioHead` in a `head.tsx`.
+In routes that load `NextStudio` ensure you have `'use client'` at the top of your file.
+
+`app/studio/[[...index]]/page.tsx`:
```tsx
+'use client'
+
import {NextStudio} from 'next-sanity/studio'
-import {StudioProvider, StudioLayout} from 'sanity'
-import config from '../sanity.config'
+import config from '../../../sanity.config'
-function StudioPage() {
- return (
-
-
- {/* Put components here and you'll have access to the same React hooks as Studio gives you when writing plugins */}
-
-
-
- )
+export default function StudioPage() {
+ // Supports the same props as `import {Studio} from 'sanity'`, `config` is required
+ return
}
```
-### Customize ` `
-
-You can still customize `_document.tsx`, the same way you would the default ` ` component from `next/document`:
+`app/studio/[[...index]]/head.tsx`:
```tsx
-import {ServerStyleSheetDocument} from 'next-sanity/studio'
-
-export default class Document extends ServerStyleSheetDocument {
- static async getInitialProps(ctx: DocumentContext) {
- // You can still override renderPage:
- const originalRenderPage = ctx.renderPage
- ctx.renderPage = () =>
- originalRenderPage({
- enhanceApp: (App) => (props) => ,
- })
-
- const initialProps = await ServerStyleSheetDocument.getInitialProps(ctx)
-
- const extraStyles = await getStyles()
- return {
- ...initialProps,
- // Add to the default styles if you want
- styles: [initialProps.styles, extraStyles],
- }
- }
- render() {
- // do the same stuff as in `next/document`
- }
+// Re-export `NextStudioHead` as default if you're happy with the default behavior
+export {NextStudioHead as default} from 'next-sanity/studio/head'
+
+// To customize it, use it as a children component:
+import {NextStudioHead} from 'next-sanity/studio/head'
+
+export default function CustomStudioHead() {
+ return (
+ <>
+
+
+ >
+ )
}
```
-### Full-control mode
+#### Next 12 or `pages/studio`
+
+Using just `NextStudio` gives you a fully working Sanity Studio v3. However we recommend also using `NextStudioHead` as it ensures CSS Media Queries that target mobile devices with display cutouts (for example iPhone's "The Notch" and "Dynamic Island") and other details.
-If you only need parts of what ` ` does for you, but not all of it.
-No problem. You can import any which one of the components that ` ` is importing and assemble them in any way you want.
+`/pages/studio/[[...index]].tsx`:
```tsx
-import {Studio, type Config} from 'sanity'
-import {NextStudioGlobalStyle, NextStudioHead} from 'next-sanity/studio'
-// This implementation will only load the bare minimum of what's required for the Studio to render correctly. No favicons, fancy tags or the like
-export default function CustomNextStudio({config}: {config: Config}) {
+import Head from 'next/head'
+import {NextStudio} from 'next-sanity/studio'
+import {NextStudioHead} from 'next-sanity/studio/head'
+
+import config from '../../sanity.config'
+
+export default function StudioPage() {
return (
<>
-
- {/* Custom extra stuff in */}
-
+
+
+
+
>
)
}
```
-And while ` ` have all features enabled by default allowing you to opt-out by giving it props, the inner components ` ` and ` ` are opt-in.
-This means that these two `StudioPage` components are functionally identical:
+### Opt-in to using `StudioProvider` and `StudioLayout`
+
+If you want to go lower level and have more control over the studio you can pass `StudioProvider` and `StudioLayout` from `sanity` as `children`:
```tsx
-import {
- NextStudio,
- NextStudioGlobalStyle,
- NextStudioHead,
- useTheme,
- useBackgroundColorsFromTheme,
-} from 'next-sanity/studio'
-import {Studio} from 'sanity'
-import config from '../sanity.config'
-
-// Turning all the features off, leaving only bare minimum required meta tags and styling
-function StudioPage() {
- return (
-
- unstable__bg=""
- unstable__noTailwindSvgFix
- unstable__noFavicons
- // an empty string turns off the tag
- unstable__document_title=""
- />
- )
-}
+import {NextStudio} from 'next-sanity/studio'
+import {StudioProvider, StudioLayout} from 'sanity'
-// Since no features are enabled it works the same way
-function Studiopage() {
- const theme = useTheme(config)
- const {themeColorLight, themeColorDark} = useBackgroundColorsFromTheme(theme)
+import config from '../../../sanity.config'
+function StudioPage() {
return (
- <>
-
-
-
- >
+
+
+ {/* Put components here and you'll have access to the same React hooks as Studio gives you when writing plugins */}
+
+
+
)
}
```
@@ -635,6 +634,51 @@ export default async function revalidate(req: NextApiRequest, res: NextApiRespon
## Migrate
+### From `v2`
+
+The `v3` release only contains breaking changes on the `next-sanity/studio` imports. If you're only using `import {createClient, groq} from 'next-sanity'` or `import {definePreview, PreviewSuspense} from 'next-sanity/preview'` then there's no migration for you to do.
+
+#### `NextStudioGlobalStyle` is removed
+
+The layout is no longer using global CSS to set the Studio height. The switch to local CSS helps interop between Next 12 and 13 layouts.
+
+#### `ServerStyleSheetDocument` is removed
+
+It's no longer necessary to setup `styled-components` SSR for the Studio to render correctly.
+
+#### The internal `isWorkspaceWithTheme` and `isWorkspaces` utils are no longer exported
+
+The `useTheme` hook is still available if you're building abstractions that need to know what the initial workspace theme variables are.
+
+#### The `useBackgroundColorsFromTheme`, `useBasePath`, `useConfigWithBasePath`, and `useTextFontFamilyFromTheme`, hooks are removed
+
+You can `useTheme` to replace `useBackgroundColorsFromTheme` and `useTextFontFamilyFromTheme`:
+
+```tsx
+import {useMemo} from 'react'
+import {useTheme} from 'next-sanity/studio'
+import type {StudioProps} from 'sanity'
+export default function MyComponent(props: Pick) {
+ const theme = useTheme(config)
+ // useBackgroundColorsFromTheme
+ const {themeColorLight, themeColorDark} = useMemo(
+ () => ({
+ themeColorLight: theme.color.light.default.base.bg,
+ themeColorDark: theme.color.dark.default.base.bg,
+ }),
+ [theme]
+ )
+ // useTextFontFamilyFromTheme
+ const fontFamily = useMemo(() => theme.fonts.text.family, [theme])
+}
+```
+
+The reason why `useBasePath` and `useConfigWithBasePath` got removed is because Next 12 and 13 diverge too much in how they declare dynamic segments. Thus you'll need to specify `basePath` in your `sanity.config.ts` manually to match the route you're loading the studio, for the time being.
+
+#### The `NextStudioHead` component has moved from `next-sanity/studio` to `next-sanity/studio/head`
+
+Its props are also quite different and it now requires you to wrap it in `import Head from 'next/head'` if you're not using a `head.tsx` in `appDir`. Make sure you use TypeScript to ease the migration.
+
### From `v1`
#### `createPreviewSubscriptionHook` is replaced with `definePreview`
diff --git a/app/page.tsx b/app/page.tsx
index 391e62e486..23aaa02935 100644
--- a/app/page.tsx
+++ b/app/page.tsx
@@ -4,6 +4,7 @@ import PreviewPosts from 'app/PreviewPosts'
import {createClient} from 'app/sanity.client'
import {PreviewSuspense} from 'app/sanity.preview'
import {previewData} from 'next/headers'
+import Link from 'next/link'
export default async function IndexPage() {
const thePreviewData = previewData()
@@ -66,12 +67,12 @@ export default async function IndexPage() {
>
)
diff --git a/app/studio/[[...tool]]/head.tsx b/app/studio/[[...tool]]/head.tsx
new file mode 100644
index 0000000000..7d18c55ecf
--- /dev/null
+++ b/app/studio/[[...tool]]/head.tsx
@@ -0,0 +1 @@
+export {NextStudioHead as default} from 'src/studio/head'
diff --git a/pages/fixtures/useConfigWithBasePath/[[...index]].tsx b/app/studio/[[...tool]]/page.tsx
similarity index 76%
rename from pages/fixtures/useConfigWithBasePath/[[...index]].tsx
rename to app/studio/[[...tool]]/page.tsx
index 4aea1e56ac..6fde3dcb24 100644
--- a/pages/fixtures/useConfigWithBasePath/[[...index]].tsx
+++ b/app/studio/[[...tool]]/page.tsx
@@ -1,10 +1,12 @@
+'use client'
+
import config from 'sanity.config'
-import {NextStudio, useConfigWithBasePath} from 'src/studio'
+import {NextStudio} from 'src/studio'
export default function StudioPage() {
return (
-///
// NOTE: This file should not be edited
// see https://nextjs.org/docs/basic-features/typescript for more information.
diff --git a/next.config.mjs b/next.config.mjs
index 8146559346..c9cf98fc8c 100644
--- a/next.config.mjs
+++ b/next.config.mjs
@@ -7,6 +7,17 @@ const nextConfig = {
// We run these checks in the CI pipeline, so we don't need to run them on Vercel
typescript: {ignoreBuildErrors: true},
eslint: {ignoreDuringBuilds: true},
+
+ // Handle static image imports the same in `npx next dev` as in `npm run build`
+ webpack: (config) => {
+ config.module.rules.push({
+ test: /\.(ico|svg|png)$/i,
+ use: [{loader: 'url-loader', options: {}}],
+ })
+
+ return config
+ },
+ images: {disableStaticImages: true},
}
export default nextConfig
diff --git a/package-lock.json b/package-lock.json
index daa05b1b50..874e09de1a 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -31,7 +31,7 @@
"@typescript-eslint/eslint-plugin": "^5.43.0",
"autoprefixer": "^10.4.13",
"eslint": "^8.28.0",
- "eslint-config-next": "13.0.5-canary.3",
+ "eslint-config-next": "13.0.5-canary.4",
"eslint-config-prettier": "^8.5.0",
"eslint-config-sanity": "^6.0.0",
"eslint-gitignore": "^0.1.0",
@@ -40,7 +40,7 @@
"groqd": "^0.0.4",
"jest": "^29.3.1",
"jest-environment-jsdom": "^29.3.1",
- "next": "13.0.5-canary.3",
+ "next": "13.0.5-canary.4",
"postcss": "^8.4.19",
"prettier": "^2.7.1",
"prettier-plugin-packagejson": "^2.3.0",
@@ -52,22 +52,31 @@
"sanity": "3.0.0-rc.2",
"styled-components": "^5.3.6",
"tailwindcss": "^3.2.4",
- "typescript": "^4.9.3"
+ "typescript": "^4.9.3",
+ "url-loader": "^4.1.1"
},
"engines": {
"node": ">=16"
},
"peerDependencies": {
+ "@sanity/icons": "*",
"@sanity/types": "*",
- "next": "^12 || ^13",
- "react": "^16.3 || ^17 || ^18",
- "sanity": "dev-preview",
- "styled-components": "^5.2"
+ "@sanity/ui": "*",
+ "next": "^13",
+ "react": "^18",
+ "sanity": "dev-preview || ^3",
+ "styled-components": "*"
},
"peerDependenciesMeta": {
+ "@sanity/icons": {
+ "optional": true
+ },
"@sanity/types": {
"optional": true
},
+ "@sanity/ui": {
+ "optional": true
+ },
"sanity": {
"optional": true
},
@@ -3181,15 +3190,15 @@
}
},
"node_modules/@next/env": {
- "version": "13.0.5-canary.3",
- "resolved": "https://registry.npmjs.org/@next/env/-/env-13.0.5-canary.3.tgz",
- "integrity": "sha512-x8hWJis9ICO6y5e7BNBPjFmFvgDRe5URd5YV3qgkw1flCRDLL3QtfTLig6nXH9DaBHaQ1a0ej7vvlzyDq6iHEg==",
+ "version": "13.0.5-canary.4",
+ "resolved": "https://registry.npmjs.org/@next/env/-/env-13.0.5-canary.4.tgz",
+ "integrity": "sha512-qHYqPwCpMbpq6d2ylu2DI3ni9vYy4Fw5TlIkQGBrQsErUVnjlsjAB7+8bLAW9tFbukYz64wp8mZiBbz2GSR13g==",
"dev": true
},
"node_modules/@next/eslint-plugin-next": {
- "version": "13.0.5-canary.3",
- "resolved": "https://registry.npmjs.org/@next/eslint-plugin-next/-/eslint-plugin-next-13.0.5-canary.3.tgz",
- "integrity": "sha512-HSho43Ul+m159QXy1ZFcA5KIKQ2J6eB35chITAEC5pREQpBLydi+XrjjwbkYiyYtpST97HgIcV0lrMXEznOi/g==",
+ "version": "13.0.5-canary.4",
+ "resolved": "https://registry.npmjs.org/@next/eslint-plugin-next/-/eslint-plugin-next-13.0.5-canary.4.tgz",
+ "integrity": "sha512-8z9lziST8sgoGHttpc56/9/m4o+L7uSZTrvXSBNsD7TzTRgFiCwAgqdJDOeSADjk3zGUlwAFMeFH/mXbDwgubw==",
"dev": true,
"dependencies": {
"glob": "7.1.7"
@@ -3216,9 +3225,9 @@
}
},
"node_modules/@next/swc-android-arm-eabi": {
- "version": "13.0.5-canary.3",
- "resolved": "https://registry.npmjs.org/@next/swc-android-arm-eabi/-/swc-android-arm-eabi-13.0.5-canary.3.tgz",
- "integrity": "sha512-t/HW4YMqsXMnyaOvgK4U5wue8sgV6+yjp7eNKVqLCDxMdXemLUIqPTdVOdfU2oVScUNYj5VQFWO52HE/x6LugQ==",
+ "version": "13.0.5-canary.4",
+ "resolved": "https://registry.npmjs.org/@next/swc-android-arm-eabi/-/swc-android-arm-eabi-13.0.5-canary.4.tgz",
+ "integrity": "sha512-WGM8AOnPr9ZS3F5ovjs2YVLOKMLf6YoxQwnR6m4EpPFaKM10MSiGDVA1BHb4ZC36joImyDVbk0t1gw6qIkdYMg==",
"cpu": [
"arm"
],
@@ -3232,9 +3241,9 @@
}
},
"node_modules/@next/swc-android-arm64": {
- "version": "13.0.5-canary.3",
- "resolved": "https://registry.npmjs.org/@next/swc-android-arm64/-/swc-android-arm64-13.0.5-canary.3.tgz",
- "integrity": "sha512-WLNjvoC9sDWqDgYOJDEPKeYwS/tymTvUDBDzQfGyl0XOW8g01wD4S1TBlHDP+D9y85BxLdwlUJoYbOwhAfwI0A==",
+ "version": "13.0.5-canary.4",
+ "resolved": "https://registry.npmjs.org/@next/swc-android-arm64/-/swc-android-arm64-13.0.5-canary.4.tgz",
+ "integrity": "sha512-Z3ARgaLWsLA78oFkcEr7jJKO/6HCUSL6Yxmv6D8VA3Pq8jS6RdhRpwjHS+mvpD8zughPPr+D72XGpu5AjHYjIg==",
"cpu": [
"arm64"
],
@@ -3248,9 +3257,9 @@
}
},
"node_modules/@next/swc-darwin-arm64": {
- "version": "13.0.5-canary.3",
- "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-13.0.5-canary.3.tgz",
- "integrity": "sha512-JeGAnktTy7fQeF8PrQJgT25LlNnRFiH6zXWVfHTRk4ufcERifaOnwpzPLjIxF6euo3qD8VynCQhYH/JsVS5HNQ==",
+ "version": "13.0.5-canary.4",
+ "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-13.0.5-canary.4.tgz",
+ "integrity": "sha512-33LjUwHXLnRut9l1aEiVlmFeMOrmfhZ0zY2OiD/8efOK/rJhrvaVT9Jlkc2m6qYcQ5KYJIGE72hP859wuamVwQ==",
"cpu": [
"arm64"
],
@@ -3264,9 +3273,9 @@
}
},
"node_modules/@next/swc-darwin-x64": {
- "version": "13.0.5-canary.3",
- "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-13.0.5-canary.3.tgz",
- "integrity": "sha512-yehsw+60uE47but0FUJD2+/06tQSsIm1/XFlFFhHv4d+bq39GB2fT+wL+h/aLK+es0yY7rx1+6Yad7CQ7a2J+Q==",
+ "version": "13.0.5-canary.4",
+ "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-13.0.5-canary.4.tgz",
+ "integrity": "sha512-Wc+hZt0HitEOONXW/5z85rUYjiRofCWazQGaQpSP8ITSjXisgOvbT1ueV9B02xiy1VvfawZBDSq2mhlgRRtgeA==",
"cpu": [
"x64"
],
@@ -3280,9 +3289,9 @@
}
},
"node_modules/@next/swc-freebsd-x64": {
- "version": "13.0.5-canary.3",
- "resolved": "https://registry.npmjs.org/@next/swc-freebsd-x64/-/swc-freebsd-x64-13.0.5-canary.3.tgz",
- "integrity": "sha512-Nd1RicUhogxTsassJNGG+4ha5ezyjJb58J6Vjs9kT5I40Iu8Mw98hCwn6VmiCtJMpY4HGJcuYtCYyXh5hY6ByQ==",
+ "version": "13.0.5-canary.4",
+ "resolved": "https://registry.npmjs.org/@next/swc-freebsd-x64/-/swc-freebsd-x64-13.0.5-canary.4.tgz",
+ "integrity": "sha512-R3rwaWAKAdDoe5YmRYu77oba0ZeOYBmOf6rGGS8V6FoMbdIalE1AOT2ZQTabiWRSqnm6QRDc8W+qOujdIUIH8Q==",
"cpu": [
"x64"
],
@@ -3296,9 +3305,9 @@
}
},
"node_modules/@next/swc-linux-arm-gnueabihf": {
- "version": "13.0.5-canary.3",
- "resolved": "https://registry.npmjs.org/@next/swc-linux-arm-gnueabihf/-/swc-linux-arm-gnueabihf-13.0.5-canary.3.tgz",
- "integrity": "sha512-t8TNq1rVBaYJz5dzAJIXgfYh7KRFich6gIT38zJ82McbKWFccuY6+9+FP6n0zkNFAZAd0CBvTQJ8fQFX6XPMog==",
+ "version": "13.0.5-canary.4",
+ "resolved": "https://registry.npmjs.org/@next/swc-linux-arm-gnueabihf/-/swc-linux-arm-gnueabihf-13.0.5-canary.4.tgz",
+ "integrity": "sha512-5yI5/Imgl6nDLZeb7CkdvyHJcg9yasgtJoQfvWVmGjaMvTXm9mBN5NzgDR3L54QfzAXm3E+6FL9YfbW2hNWTew==",
"cpu": [
"arm"
],
@@ -3312,9 +3321,9 @@
}
},
"node_modules/@next/swc-linux-arm64-gnu": {
- "version": "13.0.5-canary.3",
- "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-13.0.5-canary.3.tgz",
- "integrity": "sha512-jsHOBJSkBn247KlW2BG5j1hoLG3EIa1gE/FLCj7w/cQBCtrhb83oGBxat52Wh7UmwajrvNl13clQV0JlmkX3sg==",
+ "version": "13.0.5-canary.4",
+ "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-13.0.5-canary.4.tgz",
+ "integrity": "sha512-gd+cBGwuPwjq6ymW0VGIlVKSgcvh9mAYfmuuf5wGTNVL5q7DrjAu2vsVJucoIjND4nZmbpDMc+r3CNqP9hKpiQ==",
"cpu": [
"arm64"
],
@@ -3328,9 +3337,9 @@
}
},
"node_modules/@next/swc-linux-arm64-musl": {
- "version": "13.0.5-canary.3",
- "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-13.0.5-canary.3.tgz",
- "integrity": "sha512-AVsfjFzRKx9I4rbHBOK1WZyl3jF635uLcb7ssrJtxyMGHcr4R9KCTcl+kv7RZ2WNKVafun0pUAITKHoqKVO4fg==",
+ "version": "13.0.5-canary.4",
+ "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-13.0.5-canary.4.tgz",
+ "integrity": "sha512-U/iT04PDx389XuW0U/RMlX0esG6MMvkarhZxqzCmyov2UokmIBqQFgno5z5qeI5WU6K3d9PAHqIzM/Phc8OZHg==",
"cpu": [
"arm64"
],
@@ -3344,9 +3353,9 @@
}
},
"node_modules/@next/swc-linux-x64-gnu": {
- "version": "13.0.5-canary.3",
- "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-13.0.5-canary.3.tgz",
- "integrity": "sha512-E83c9cs+8hwvpTUcn991ec/Q6tDG54SzvVrvNqAj23yiWGfHx0MDt1NqYtF0WOJ7W+6G6PtlD9SR0KKiaPMSeA==",
+ "version": "13.0.5-canary.4",
+ "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-13.0.5-canary.4.tgz",
+ "integrity": "sha512-6wFkzS4T/zMr26obiF4tilSeDKpNJ6zL0RhVwKwE4CoqbuGb7DFwLagJ08X1+uJq+iL+zI1YZ5uK2aSgBSXm1w==",
"cpu": [
"x64"
],
@@ -3360,9 +3369,9 @@
}
},
"node_modules/@next/swc-linux-x64-musl": {
- "version": "13.0.5-canary.3",
- "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-13.0.5-canary.3.tgz",
- "integrity": "sha512-d9CT6qNDxf/C2j8oU6sb2XK2C0qE5N6uI+JL2r19fZKgQIgjCMEWbuyI2oVpq/LrwJ4w0vmjxuRg/9YEb8zX9w==",
+ "version": "13.0.5-canary.4",
+ "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-13.0.5-canary.4.tgz",
+ "integrity": "sha512-ylWljx2V5XXDLWw2BTOk8AXP6nq7DNgGM6KCBUGivRVwXzdGBUt4kLu5kblpBHCg8357eXCpqF3MVdw1DLOk7g==",
"cpu": [
"x64"
],
@@ -3376,9 +3385,9 @@
}
},
"node_modules/@next/swc-win32-arm64-msvc": {
- "version": "13.0.5-canary.3",
- "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-13.0.5-canary.3.tgz",
- "integrity": "sha512-4Mcw9pc8HgZ0FoOybyfDW17dXXgJnqs8TDXbWDlV9tGrbYJ5yPPe2r0gZaPCGCtbYLLz3XQfd6kDDf2J0hx7Kw==",
+ "version": "13.0.5-canary.4",
+ "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-13.0.5-canary.4.tgz",
+ "integrity": "sha512-cQfyuXRGuWIrbXVkA0PHgOf6858a8du2lxgznjmjolLrBLFIHCZsDOFjYgFLC25cAGFDVhHH94F6E+o1RoDxoQ==",
"cpu": [
"arm64"
],
@@ -3392,9 +3401,9 @@
}
},
"node_modules/@next/swc-win32-ia32-msvc": {
- "version": "13.0.5-canary.3",
- "resolved": "https://registry.npmjs.org/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-13.0.5-canary.3.tgz",
- "integrity": "sha512-vGHnW+l+aE264lmz7warC7FPEUb3RDypooKZHDb7fTtlbqnEBf8MVlPYLVj73MSwbW8vKfoatScCG09tljYztA==",
+ "version": "13.0.5-canary.4",
+ "resolved": "https://registry.npmjs.org/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-13.0.5-canary.4.tgz",
+ "integrity": "sha512-JQvizP49QnkXfBYp+L+Gf9txV5fOwUqBlCRA/44N3kWnkrhs/oceivI/l1dw8OUMzwtwHCrTyjY4tJbNYZ3aMA==",
"cpu": [
"ia32"
],
@@ -3408,9 +3417,9 @@
}
},
"node_modules/@next/swc-win32-x64-msvc": {
- "version": "13.0.5-canary.3",
- "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-13.0.5-canary.3.tgz",
- "integrity": "sha512-BvqxBWnY2D0DDBh+NxSQ70+f/kLEEQAbkbyjOcKIxjtYY9Gu6FHAHHyyTNwXMRKr7X3yiX/olLTy1EekCZ0M6A==",
+ "version": "13.0.5-canary.4",
+ "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-13.0.5-canary.4.tgz",
+ "integrity": "sha512-POPU0u5+1kh2afknud/rqfxN9tBdJtLNYEEPrSO8hSGMlWGv+Mzqnr/xtcb1lHOagXjNRSSjJ2T2DGTyu8Aj6A==",
"cpu": [
"x64"
],
@@ -7160,6 +7169,15 @@
"url": "https://github.com/sponsors/epoberezkin"
}
},
+ "node_modules/ajv-keywords": {
+ "version": "3.5.2",
+ "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz",
+ "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==",
+ "dev": true,
+ "peerDependencies": {
+ "ajv": "^6.9.1"
+ }
+ },
"node_modules/ansi-escapes": {
"version": "4.3.2",
"resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz",
@@ -7743,6 +7761,15 @@
"integrity": "sha512-NzUnlZexiaH/46WDhANlyR2bXRopNg4F/zuSA3OpZnllCUgRaOF2znDioDWrmbNVsuZk6l9pMquQB38cfBZwkQ==",
"dev": true
},
+ "node_modules/big.js": {
+ "version": "5.2.2",
+ "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz",
+ "integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==",
+ "dev": true,
+ "engines": {
+ "node": "*"
+ }
+ },
"node_modules/binary-extensions": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz",
@@ -9200,6 +9227,15 @@
"integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==",
"dev": true
},
+ "node_modules/emojis-list": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-3.0.0.tgz",
+ "integrity": "sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==",
+ "dev": true,
+ "engines": {
+ "node": ">= 4"
+ }
+ },
"node_modules/end-of-stream": {
"version": "1.4.4",
"resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz",
@@ -9844,12 +9880,12 @@
}
},
"node_modules/eslint-config-next": {
- "version": "13.0.5-canary.3",
- "resolved": "https://registry.npmjs.org/eslint-config-next/-/eslint-config-next-13.0.5-canary.3.tgz",
- "integrity": "sha512-fgEDrlAx2Em3bsifMJ9MkJkfxQkh7UM5Bb4D7QHHhYgExRYuhmFGPCGRKw9EXNN0fvnLmd1vSIj1EXMSzA9YgQ==",
+ "version": "13.0.5-canary.4",
+ "resolved": "https://registry.npmjs.org/eslint-config-next/-/eslint-config-next-13.0.5-canary.4.tgz",
+ "integrity": "sha512-8PMhPj9BvDJVoEVN8//MCGDy6p+NrZ3L48fsukq74ISMMkukHjt9gvYDaI1qJRGb5UmLr47MX2mLUSsCYvJAEg==",
"dev": true,
"dependencies": {
- "@next/eslint-plugin-next": "13.0.5-canary.3",
+ "@next/eslint-plugin-next": "13.0.5-canary.4",
"@rushstack/eslint-patch": "^1.1.3",
"@typescript-eslint/parser": "^5.42.0",
"eslint-import-resolver-node": "^0.3.6",
@@ -11497,20 +11533,6 @@
"integrity": "sha512-COpmrF2NOg4TBWUJ5UVyaCU2A88wEMkUPK4hNqyCkqHbxT92BbvfjoSozkAIIm6XhicGlJHhFdullInrdhwU8Q==",
"dev": true
},
- "node_modules/history": {
- "version": "4.10.1",
- "resolved": "https://registry.npmjs.org/history/-/history-4.10.1.tgz",
- "integrity": "sha512-36nwAD620w12kuzPAsyINPWJqlNbij+hpK1k9XRloDtym8mxzGYl2c17LnV6IAGB2Dmg4tEa7G7DlawS0+qjew==",
- "dev": true,
- "dependencies": {
- "@babel/runtime": "^7.1.2",
- "loose-envify": "^1.2.0",
- "resolve-pathname": "^3.0.0",
- "tiny-invariant": "^1.0.2",
- "tiny-warning": "^1.0.0",
- "value-equal": "^1.0.1"
- }
- },
"node_modules/hoist-non-react-statics": {
"version": "3.3.2",
"resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz",
@@ -14308,6 +14330,20 @@
"node": ">=4"
}
},
+ "node_modules/loader-utils": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.4.tgz",
+ "integrity": "sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw==",
+ "dev": true,
+ "dependencies": {
+ "big.js": "^5.2.2",
+ "emojis-list": "^3.0.0",
+ "json5": "^2.1.2"
+ },
+ "engines": {
+ "node": ">=8.9.0"
+ }
+ },
"node_modules/locate-path": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz",
@@ -14924,12 +14960,12 @@
"dev": true
},
"node_modules/next": {
- "version": "13.0.5-canary.3",
- "resolved": "https://registry.npmjs.org/next/-/next-13.0.5-canary.3.tgz",
- "integrity": "sha512-u3As6SkLXf2u9Mt06B3gBdQzVi45qoH27fyj2UGH+GcWjloz0x0Q+99CEz0sr93zS6iQRZuDn93PMZXoXIa8Og==",
+ "version": "13.0.5-canary.4",
+ "resolved": "https://registry.npmjs.org/next/-/next-13.0.5-canary.4.tgz",
+ "integrity": "sha512-EW6TvRv/paH48MhiPxxpRHIucFjJCcHXiBL/23IG3P/vOO5kJi76/HUeMDowcsv7UmrZAhwndLlnS5sOQplojw==",
"dev": true,
"dependencies": {
- "@next/env": "13.0.5-canary.3",
+ "@next/env": "13.0.5-canary.4",
"@swc/helpers": "0.4.14",
"caniuse-lite": "^1.0.30001406",
"postcss": "8.4.14",
@@ -14943,19 +14979,19 @@
"node": ">=14.6.0"
},
"optionalDependencies": {
- "@next/swc-android-arm-eabi": "13.0.5-canary.3",
- "@next/swc-android-arm64": "13.0.5-canary.3",
- "@next/swc-darwin-arm64": "13.0.5-canary.3",
- "@next/swc-darwin-x64": "13.0.5-canary.3",
- "@next/swc-freebsd-x64": "13.0.5-canary.3",
- "@next/swc-linux-arm-gnueabihf": "13.0.5-canary.3",
- "@next/swc-linux-arm64-gnu": "13.0.5-canary.3",
- "@next/swc-linux-arm64-musl": "13.0.5-canary.3",
- "@next/swc-linux-x64-gnu": "13.0.5-canary.3",
- "@next/swc-linux-x64-musl": "13.0.5-canary.3",
- "@next/swc-win32-arm64-msvc": "13.0.5-canary.3",
- "@next/swc-win32-ia32-msvc": "13.0.5-canary.3",
- "@next/swc-win32-x64-msvc": "13.0.5-canary.3"
+ "@next/swc-android-arm-eabi": "13.0.5-canary.4",
+ "@next/swc-android-arm64": "13.0.5-canary.4",
+ "@next/swc-darwin-arm64": "13.0.5-canary.4",
+ "@next/swc-darwin-x64": "13.0.5-canary.4",
+ "@next/swc-freebsd-x64": "13.0.5-canary.4",
+ "@next/swc-linux-arm-gnueabihf": "13.0.5-canary.4",
+ "@next/swc-linux-arm64-gnu": "13.0.5-canary.4",
+ "@next/swc-linux-arm64-musl": "13.0.5-canary.4",
+ "@next/swc-linux-x64-gnu": "13.0.5-canary.4",
+ "@next/swc-linux-x64-musl": "13.0.5-canary.4",
+ "@next/swc-win32-arm64-msvc": "13.0.5-canary.4",
+ "@next/swc-win32-ia32-msvc": "13.0.5-canary.4",
+ "@next/swc-win32-x64-msvc": "13.0.5-canary.4"
},
"peerDependencies": {
"fibers": ">= 3.1.0",
@@ -20512,6 +20548,20 @@
"node": ">=8"
}
},
+ "node_modules/sanity/node_modules/history": {
+ "version": "4.10.1",
+ "resolved": "https://registry.npmjs.org/history/-/history-4.10.1.tgz",
+ "integrity": "sha512-36nwAD620w12kuzPAsyINPWJqlNbij+hpK1k9XRloDtym8mxzGYl2c17LnV6IAGB2Dmg4tEa7G7DlawS0+qjew==",
+ "dev": true,
+ "dependencies": {
+ "@babel/runtime": "^7.1.2",
+ "loose-envify": "^1.2.0",
+ "resolve-pathname": "^3.0.0",
+ "tiny-invariant": "^1.0.2",
+ "tiny-warning": "^1.0.0",
+ "value-equal": "^1.0.1"
+ }
+ },
"node_modules/sanity/node_modules/json5": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz",
@@ -20630,6 +20680,24 @@
"loose-envify": "^1.1.0"
}
},
+ "node_modules/schema-utils": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.1.1.tgz",
+ "integrity": "sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw==",
+ "dev": true,
+ "dependencies": {
+ "@types/json-schema": "^7.0.8",
+ "ajv": "^6.12.5",
+ "ajv-keywords": "^3.5.2"
+ },
+ "engines": {
+ "node": ">= 10.13.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/webpack"
+ }
+ },
"node_modules/scroll-into-view-if-needed": {
"version": "2.2.29",
"resolved": "https://registry.npmjs.org/scroll-into-view-if-needed/-/scroll-into-view-if-needed-2.2.29.tgz",
@@ -22178,6 +22246,33 @@
"integrity": "sha512-jk1+QP6ZJqyOiuEI9AEWQfju/nB2Pw466kbA0LEZljHwKeMgd9WrAEgEGxjPDD2+TNbbb37rTyhEfrCXfuKXnA==",
"dev": true
},
+ "node_modules/url-loader": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/url-loader/-/url-loader-4.1.1.tgz",
+ "integrity": "sha512-3BTV812+AVHHOJQO8O5MkWgZ5aosP7GnROJwvzLS9hWDj00lZ6Z0wNak423Lp9PBZN05N+Jk/N5Si8jRAlGyWA==",
+ "dev": true,
+ "dependencies": {
+ "loader-utils": "^2.0.0",
+ "mime-types": "^2.1.27",
+ "schema-utils": "^3.0.0"
+ },
+ "engines": {
+ "node": ">= 10.13.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/webpack"
+ },
+ "peerDependencies": {
+ "file-loader": "*",
+ "webpack": "^4.0.0 || ^5.0.0"
+ },
+ "peerDependenciesMeta": {
+ "file-loader": {
+ "optional": true
+ }
+ }
+ },
"node_modules/url-parse": {
"version": "1.5.10",
"resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.10.tgz",
diff --git a/package.config.ts b/package.config.ts
index 805569887b..68279ae988 100644
--- a/package.config.ts
+++ b/package.config.ts
@@ -18,7 +18,7 @@ export default defineConfig({
url({
fileName: '[dirname][hash][extname]',
sourceDir: path.join(__dirname, 'src'),
- include: ['**/*.ico', '**/*.svg', '**/*.png', '**/*.jp(e)?g', '**/*.gif', '**/*.webp'],
+ include: ['**/*.ico', '**/*.svg', '**/*.png'],
}),
],
},
diff --git a/package.json b/package.json
index a6ba718bb8..19f69195f4 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "next-sanity",
- "version": "2.1.0",
+ "version": "3.0.0-app-dir.3",
"description": "Sanity.io toolkit for Next.js",
"keywords": [
"sanity",
@@ -39,16 +39,23 @@
"default": "./dist/preview.js"
},
"./studio": {
- "types": "./dist/studio.d.ts",
+ "types": "./dist/studio/index.d.ts",
"source": "./src/studio/index.ts",
"node": {
"source": "./src/studio/index.ts",
- "import": "./dist/studio.mjs",
- "require": "./dist/studio.cjs"
+ "import": "./dist/studio/index.mjs",
+ "require": "./dist/studio/index.cjs"
},
- "import": "./dist/studio.js",
- "require": "./dist/studio.cjs",
- "default": "./dist/studio.js"
+ "import": "./dist/studio/index.js",
+ "require": "./dist/studio/index.cjs",
+ "default": "./dist/studio/index.js"
+ },
+ "./studio/head": {
+ "types": "./dist/studio/head.d.ts",
+ "source": "./src/studio/head/index.ts",
+ "import": "./dist/studio/head.js",
+ "require": "./dist/studio/head.cjs",
+ "default": "./dist/studio/head.js"
},
"./webhook": {
"types": "./dist/webhook.d.ts",
@@ -69,7 +76,10 @@
"./dist/preview.d.ts"
],
"studio": [
- "./dist/studio.d.ts"
+ "./dist/studio/index.d.ts"
+ ],
+ "studio/head": [
+ "./dist/studio/head.d.ts"
],
"webhook": [
"./dist/webhook.d.ts"
@@ -85,7 +95,7 @@
"prebuild": "npm run clean",
"build": "pkg build --tsconfig tsconfig.build.json --strict",
"postbuild": "npm run build:studio.mjs",
- "build:studio.mjs": "cp studio.mjs dist/studio.mjs",
+ "build:studio.mjs": "cp studio.mjs dist/studio/index.mjs",
"clean": "rimraf dist",
"coverage": "npm test -- --coverage",
"dev": "next",
@@ -96,7 +106,7 @@
"test": "jest",
"test:node-esm-cjs": "node test.mjs && node test.cjs",
"type-check": "tsc --noEmit",
- "update:icons": "cp node_modules/@sanity/server/lib/static/favicons/* src/studio && cp src/studio/favicon-{192,512}.png public && cp src/studio/webmanifest.json public/manifest.webmanifest"
+ "update:icons": "cp node_modules/@sanity/server/static/favicons/{apple-touch-icon.png,favicon.ico,favicon.svg} src/studio/head"
},
"browserslist": [
"> 0.2% and supports es6-module and supports es6-module-dynamic-import and not dead and not IE 11",
@@ -131,7 +141,7 @@
"@typescript-eslint/eslint-plugin": "^5.43.0",
"autoprefixer": "^10.4.13",
"eslint": "^8.28.0",
- "eslint-config-next": "13.0.5-canary.3",
+ "eslint-config-next": "13.0.5-canary.4",
"eslint-config-prettier": "^8.5.0",
"eslint-config-sanity": "^6.0.0",
"eslint-gitignore": "^0.1.0",
@@ -140,7 +150,7 @@
"groqd": "^0.0.4",
"jest": "^29.3.1",
"jest-environment-jsdom": "^29.3.1",
- "next": "13.0.5-canary.3",
+ "next": "13.0.5-canary.4",
"postcss": "^8.4.19",
"prettier": "^2.7.1",
"prettier-plugin-packagejson": "^2.3.0",
@@ -152,19 +162,28 @@
"sanity": "3.0.0-rc.2",
"styled-components": "^5.3.6",
"tailwindcss": "^3.2.4",
- "typescript": "^4.9.3"
+ "typescript": "^4.9.3",
+ "url-loader": "^4.1.1"
},
"peerDependencies": {
+ "@sanity/icons": "*",
"@sanity/types": "*",
- "next": "^12 || ^13",
- "react": "^16.3 || ^17 || ^18",
- "sanity": "dev-preview",
- "styled-components": "^5.2"
+ "@sanity/ui": "*",
+ "next": "^13",
+ "react": "^18",
+ "sanity": "dev-preview || ^3",
+ "styled-components": "*"
},
"peerDependenciesMeta": {
+ "@sanity/icons": {
+ "optional": true
+ },
"@sanity/types": {
"optional": true
},
+ "@sanity/ui": {
+ "optional": true
+ },
"sanity": {
"optional": true
},
diff --git a/pages/_document.tsx b/pages/_document.tsx
deleted file mode 100644
index 000907d953..0000000000
--- a/pages/_document.tsx
+++ /dev/null
@@ -1,3 +0,0 @@
-import {ServerStyleSheetDocument} from 'src/studio'
-
-export default class Document extends ServerStyleSheetDocument {}
diff --git a/pages/studio/[[...tool]].tsx b/pages/studio/[[...tool]].tsx
deleted file mode 100644
index 030dc595ff..0000000000
--- a/pages/studio/[[...tool]].tsx
+++ /dev/null
@@ -1,15 +0,0 @@
-import _config from 'sanity.config'
-import {NextStudio, useConfigWithBasePath} from 'src/studio'
-
-export default function StudioPage() {
- const config = useConfigWithBasePath(_config)
-
- return (
-
- )
-}
diff --git a/public/favicon-192.png b/public/favicon-192.png
deleted file mode 100644
index 1323376b82..0000000000
Binary files a/public/favicon-192.png and /dev/null differ
diff --git a/public/favicon-512.png b/public/favicon-512.png
deleted file mode 100644
index 6320b50010..0000000000
Binary files a/public/favicon-512.png and /dev/null differ
diff --git a/public/manifest.webmanifest b/public/manifest.webmanifest
deleted file mode 100644
index b55b317f4c..0000000000
--- a/public/manifest.webmanifest
+++ /dev/null
@@ -1,14 +0,0 @@
-{
- "icons": [
- {
- "src": "./favicon-192.png",
- "type": "image/png",
- "sizes": "192x192"
- },
- {
- "src": "./favicon-512.png",
- "type": "image/png",
- "sizes": "512x512"
- }
- ]
-}
diff --git a/sanity.cli.ts b/sanity.cli.ts
index 669f9c6b8c..ddb9b439a0 100644
--- a/sanity.cli.ts
+++ b/sanity.cli.ts
@@ -1,6 +1,6 @@
/* eslint-disable no-process-env */
import {loadEnvConfig} from '@next/env'
-import {createCliConfig} from 'sanity/cli'
+import {defineCliConfig} from 'sanity/cli'
const dev = process.env.NODE_ENV !== 'production'
loadEnvConfig(__dirname, dev, {info: () => null, error: console.error})
@@ -8,4 +8,4 @@ loadEnvConfig(__dirname, dev, {info: () => null, error: console.error})
const projectId = process.env.NEXT_PUBLIC_SANITY_PROJECT_ID
const dataset = process.env.NEXT_PUBLIC_SANITY_DATASET
-export default createCliConfig({api: {projectId, dataset}})
+export default defineCliConfig({api: {projectId, dataset}})
diff --git a/sanity.config.ts b/sanity.config.ts
index ac2bf47b5e..4fe0b3a500 100644
--- a/sanity.config.ts
+++ b/sanity.config.ts
@@ -10,6 +10,7 @@ const dataset = process.env.NEXT_PUBLIC_SANITY_DATASET!
export default defineConfig({
title: 'next-sanity',
+ basePath: '/studio',
projectId,
dataset,
diff --git a/src/studio/LazyStudio.tsx b/src/studio/LazyStudio.tsx
new file mode 100644
index 0000000000..1d0e101ed0
--- /dev/null
+++ b/src/studio/LazyStudio.tsx
@@ -0,0 +1 @@
+export {Studio as default} from 'sanity'
diff --git a/src/studio/NextStudio.tsx b/src/studio/NextStudio.tsx
index a6dae283af..4354aa6b0e 100644
--- a/src/studio/NextStudio.tsx
+++ b/src/studio/NextStudio.tsx
@@ -1,54 +1,28 @@
-import {memo} from 'react'
-import {type StudioProps, Studio} from 'sanity'
+import {lazy, memo} from 'react'
+import type {StudioProps} from 'sanity'
-import {NextStudioGlobalStyle, NextStudioGlobalStyleProps} from './NextStudioGlobalStyle'
-import {type NextStudioHeadProps, NextStudioHead} from './NextStudioHead'
+import {NextStudioFallback} from './NextStudioFallback'
+import {type NextStudioLayoutProps, NextStudioLayout} from './NextStudioLayout'
import {NextStudioNoScript} from './NextStudioNoScript'
-import {useBackgroundColorsFromTheme, useTextFontFamilyFromTheme, useTheme} from './utils'
+import {NextStudioSuspense} from './NextStudioSuspense'
+
+const Studio = memo(lazy(() => import('./LazyStudio')))
/** @beta */
export interface NextStudioProps extends StudioProps {
children?: React.ReactNode
/**
- * Turns off the default global styling
- * @alpha
- */
- unstable__noGlobalStyle?: boolean
- /**
- * Apply fix with SVG icon centering that happens if TailwindCSS is loaded, on by default
- * @alpha
- */
- unstable__noTailwindSvgFix?: NextStudioGlobalStyleProps['unstable__tailwindSvgFix']
- /**
- * Add stuff to the head with next/head
- * @alpha
- */
- unstable__head?: NextStudioHeadProps['children']
- /**
- * Sets the document title
- * @alpha
- */
- unstable__document_title?: NextStudioHeadProps['title']
- /**
- * Sets the background color of
- * @alpha
- */
- unstable__bg?: NextStudioGlobalStyleProps['bg']
- /**
- * Sets the font-family of #__next
- * @alpha
- */
- unstable__fontFamily?: NextStudioGlobalStyleProps['fontFamily']
- /**
- * Don't load the favicon meta tags
+ * Apply fix with SVG icon centering that happens if TailwindCSS is loaded
+ * @defaultValue true
* @alpha
*/
- unstable__noFavicons?: boolean
+ unstable__tailwindSvgFix?: NextStudioLayoutProps['unstable__tailwindSvgFix']
/**
- * Don't render the tag
+ * Render the tag
+ * @defaultValue true
* @alpha
*/
- unstable__noNoScript?: boolean
+ unstable__noScript?: boolean
}
/**
* Intended to render at the root of a page, letting the Studio own that page and render much like it would if you used `npx sanity start` to render
@@ -57,38 +31,25 @@ export interface NextStudioProps extends StudioProps {
const NextStudioComponent = ({
children,
config,
- unstable__noGlobalStyle,
- unstable__noTailwindSvgFix,
- unstable__head,
- unstable__document_title,
- unstable__bg,
- unstable__fontFamily,
- unstable__noFavicons,
- unstable__noNoScript,
+ unstable__tailwindSvgFix = true,
+ unstable__noScript = true,
+ scheme,
...props
}: NextStudioProps) => {
- const theme = useTheme(config)
- const {themeColorLight, themeColorDark} = useBackgroundColorsFromTheme(theme)
- const themeFontFamily = useTextFontFamilyFromTheme(theme)
return (
<>
- {children || }
-
- {unstable__head}
-
- {!unstable__noGlobalStyle && (
-
- )}
- {!unstable__noNoScript && }
+ {!unstable__noScript && }
+ }>
+
+ {children || (
+
+ )}
+
+
>
)
}
diff --git a/src/studio/NextStudioFallback.tsx b/src/studio/NextStudioFallback.tsx
new file mode 100644
index 0000000000..28072bdb68
--- /dev/null
+++ b/src/studio/NextStudioFallback.tsx
@@ -0,0 +1,86 @@
+// Intentionally not using `styled-components` to ensure it works in any `next` setup.
+// Wether 'styled-components' SSR is setup or not.
+
+import {SpinnerIcon} from '@sanity/icons'
+import {_responsive, rem} from '@sanity/ui'
+import {memo} from 'react'
+import type {StudioProps} from 'sanity'
+
+import {useTheme} from './useTheme'
+
+/** @alpha */
+export type NextStudioFallbackProps = Pick
+
+const keyframes = `
+from {
+ transform: rotate(0deg);
+}
+
+to {
+ transform: rotate(360deg);
+}
+`
+
+function NextStudioFallbackComponent(props: NextStudioFallbackProps) {
+ const {config, scheme = 'light'} = props
+ const id = 'next-sanity-spinner'
+ const theme = useTheme(config)
+ const {fonts, media} = theme
+
+ const styles: any = _responsive(media, [2], (size: number) => {
+ const {ascenderHeight, descenderHeight, lineHeight, iconSize} = fonts.text.sizes[size]
+ const capHeight = lineHeight - ascenderHeight - descenderHeight
+
+ return {
+ wrapper: {
+ animation: `${id} 500ms linear infinite`,
+ color: theme.color[scheme].default.muted.default.enabled.muted.fg,
+ width: rem(capHeight),
+ height: rem(capHeight),
+ },
+ svg: {
+ display: 'block',
+ width: rem(iconSize),
+ height: rem(iconSize),
+ margin: (capHeight - iconSize) / 2,
+ },
+ }
+ })[0]
+
+ return (
+
+ )
+}
+
+/** @alpha */
+export const NextStudioFallback = memo(NextStudioFallbackComponent)
diff --git a/src/studio/NextStudioGlobalStyle.ts b/src/studio/NextStudioGlobalStyle.ts
deleted file mode 100644
index 6478ff1d5a..0000000000
--- a/src/studio/NextStudioGlobalStyle.ts
+++ /dev/null
@@ -1,45 +0,0 @@
-import {createGlobalStyle, css} from 'styled-components'
-
-/** @alpha */
-export interface NextStudioGlobalStyleProps {
- fontFamily?: string
- bg?: string
- unstable__tailwindSvgFix?: boolean
-}
-/** @alpha */
-export const NextStudioGlobalStyle = createGlobalStyle`
-${({bg}) =>
- bg
- ? css`
- html {
- background-color: ${bg};
- }
- `
- : ''}
-html,
-body,
-#__next {
- height: 100%;
-}
-body {
- margin: 0;
- overscroll-behavior: none;
- -webkit-font-smoothing: antialiased;
-}
-${({fontFamily}) =>
- fontFamily
- ? css`
- #__next {
- font-family: ${fontFamily};
- }
- `
- : ''}
-${({unstable__tailwindSvgFix}) =>
- unstable__tailwindSvgFix
- ? css`
- /* override tailwind reset */
- :root svg {
- display: inline;
- }
- `
- : ''}`
diff --git a/src/studio/NextStudioHead.tsx b/src/studio/NextStudioHead.tsx
deleted file mode 100644
index 082f252782..0000000000
--- a/src/studio/NextStudioHead.tsx
+++ /dev/null
@@ -1,110 +0,0 @@
-/* eslint-disable no-process-env */
-import _Head from 'next/head'
-import {type ReactNode, memo, useCallback} from 'react'
-
-// @ts-ignore
-import iconApple from './apple-touch-icon.png'
-// @ts-ignore
-import iconIco from './favicon.ico'
-// @ts-ignore
-import iconSvg from './favicon.svg'
-// @ts-ignore
-import icon192 from './favicon-192.png'
-// @ts-ignore
-import icon512 from './favicon-512.png'
-import type {MetaThemeColors} from './utils'
-import webmanifest from './webmanifest.json'
-
-// Workaround ESM + CJS interop issues
-/** @internal */
-const Head = ('default' in _Head ? _Head.default : _Head) as typeof _Head
-
-// Interop between how Parcel and Next deals with asset imports
-const interop = (href: string | {src: string}): string =>
- typeof href === 'string' ? href : href.src
-
-/** @alpha */
-export interface NextStudioHeadProps extends Partial {
- children?: ReactNode
- title?: string
- favicons?: boolean
-}
-const NextStudioHeadComponent = ({
- children,
- themeColorDark,
- themeColorLight,
- title = 'Sanity Studio',
- favicons,
-}: NextStudioHeadProps) => {
- const inlineWebmanifest = useCallback(() => {
- const manifest = JSON.parse(JSON.stringify(webmanifest))
- const icons = manifest.icons.map((icon: any) => {
- // Inline manifests works best when URLs are absolute
- const src =
- // eslint-disable-next-line no-nested-ternary
- icon.src === './favicon-192.png'
- ? interop(icon192)
- : icon.src === './favicon-512.png'
- ? interop(icon512)
- : icon.src
- return {
- ...icon,
- src: process.env.NEXT_PUBLIC_VERCEL_URL
- ? new URL(src, `https://${process.env.NEXT_PUBLIC_VERCEL_URL}`).toString()
- : src,
- }
- })
- return `data:application/manifest+json,${encodeURIComponent(
- JSON.stringify({...manifest, icons})
- )}`
- }, [])
-
- return (
-
-
-
-
- {title && {title} }
- {favicons && }
- {favicons && }
- {favicons && }
- {favicons && (
-
- )}
- {/* These theme-color tags makes the Studio look really really good on devices like iPads as the browser chrome adopts the Studio background */}
- {themeColorLight && (
-
- )}
- {themeColorDark && (
-
- )}
- {children}
-
- )
-}
-
-/** @alpha */
-export const NextStudioHead = memo(NextStudioHeadComponent)
diff --git a/src/studio/NextStudioLayout.tsx b/src/studio/NextStudioLayout.tsx
new file mode 100644
index 0000000000..e0235dfd05
--- /dev/null
+++ b/src/studio/NextStudioLayout.tsx
@@ -0,0 +1,64 @@
+/* eslint-disable camelcase */
+import {memo} from 'react'
+import type {StudioProps} from 'sanity'
+import styled, {css} from 'styled-components'
+
+import {useTheme} from './useTheme'
+
+/** @alpha */
+export interface NextStudioLayoutProps extends Pick {
+ children: React.ReactNode
+ /**
+ * Apply fix with SVG icon centering that happens if TailwindCSS is loaded
+ * @defaultValue true
+ */
+ unstable__tailwindSvgFix?: boolean
+}
+
+type LayoutProps = {
+ $unstable__tailwindSvgFix: NextStudioLayoutProps['unstable__tailwindSvgFix']
+ $bg: string
+ $fontFamily: string
+}
+const Layout = styled.div`
+ font-family: ${({$fontFamily}) => $fontFamily};
+ background-color: ${({$bg}: any) => $bg};
+ height: 100vh;
+ max-height: 100dvh;
+ overscroll-behavior: none;
+ -webkit-font-smoothing: antialiased;
+ overflow: auto;
+
+ ${({$unstable__tailwindSvgFix}: any) =>
+ $unstable__tailwindSvgFix
+ ? css`
+ /* override tailwind reset */
+ *:not([data-ui='Popover__arrow']):not([data-ui='Tooltip__arrow']) > svg {
+ display: inline;
+ }
+ `
+ : ''}
+`
+
+const NextStudioLayoutComponent = ({
+ children,
+ config,
+ scheme = 'light',
+ unstable__tailwindSvgFix = true,
+}: NextStudioLayoutProps) => {
+ const theme = useTheme(config)
+
+ return (
+
+ {children}
+
+ )
+}
+
+/** @alpha */
+export const NextStudioLayout = memo(NextStudioLayoutComponent)
diff --git a/src/studio/NextStudioNoScript.tsx b/src/studio/NextStudioNoScript.tsx
index 076fc611bc..3d3fa2ef66 100644
--- a/src/studio/NextStudioNoScript.tsx
+++ b/src/studio/NextStudioNoScript.tsx
@@ -9,6 +9,7 @@ const style = {
left: 0;
bottom: 0;
background: #fff;
+ z-index: 1;
}
.sanity-app-no-js__content {
diff --git a/src/studio/NextStudioSuspense.tsx b/src/studio/NextStudioSuspense.tsx
new file mode 100644
index 0000000000..a17a319c8d
--- /dev/null
+++ b/src/studio/NextStudioSuspense.tsx
@@ -0,0 +1,15 @@
+import {type ReactNode, Suspense, useEffect, useReducer} from 'react'
+
+/** @alpha */
+export type NextStudioSuspenseProps = {
+ children: ReactNode
+ fallback: ReactNode
+}
+
+/** @alpha */
+export function NextStudioSuspense({children, fallback}: NextStudioSuspenseProps) {
+ const [mounted, mount] = useReducer(() => true, false)
+ useEffect(mount, [mount])
+
+ return {mounted ? children : fallback}
+}
diff --git a/src/studio/ServerStyleSheetDocument.tsx b/src/studio/ServerStyleSheetDocument.tsx
deleted file mode 100644
index 05caab10e2..0000000000
--- a/src/studio/ServerStyleSheetDocument.tsx
+++ /dev/null
@@ -1,62 +0,0 @@
-// We can disable this rule safely as we're not trying to use it outside pages/document, we're shipping a wrapper
-// eslint-disable-next-line @next/next/no-document-import-in-page
-import _Document, {type DocumentContext} from 'next/document'
-import {ServerStyleSheet} from 'styled-components'
-
-// Workaround ESM + CJS interop issues
-/** @internal */
-const Document = ('default' in _Document ? _Document.default : _Document) as typeof _Document
-
-/**
- * Usage, from a pages/_document.tsx file:
- * ```
- * import {ServerStyleSheetDocument} from 'next-sanity/studio'
- *
- * export default class MyDocument extends ServerStyleSheetDocument {}
- * ```
- *
- * To do extra stuff in getInitialProps:
- * ```
- * import {ServerStyleSheetDocument} from 'next-sanity/studio'
- * import { type DocumentContext } from 'next/document'
- *
- * export default class MyDocument extends ServerStyleSheetDocument {
- * static async getInitialProps(ctx: DocumentContext) {
- * // You can still override renderPage:
- * const originalRenderPage = ctx.renderPage
- * ctx.renderPage = () => originalRenderPage({
- * enhanceApp: (App) => (props) =>
- * })
- *
- * const initialProps = await ServerStyleSheetDocument.getInitialProps(ctx)
- * const extraStyles = await getStyles()
- * return {
- * ...initialProps,
- * styles: [initialProps.styles, extraStyles],
- * }
- * }
- * }
- * ```
- * @beta
- */
-export class ServerStyleSheetDocument extends Document {
- static async getInitialProps(ctx: DocumentContext) {
- const sheet = new ServerStyleSheet()
- const originalRenderPage = ctx.renderPage
-
- try {
- ctx.renderPage = () =>
- originalRenderPage({
- enhanceApp: (App) => (props) => sheet.collectStyles( ),
- })
-
- const initialProps = await Document.getInitialProps(ctx)
- return {
- ...initialProps,
- styles: [initialProps.styles, sheet.getStyleElement()],
- }
- } finally {
- sheet.seal()
- }
- }
-}
diff --git a/src/studio/favicon-192.png b/src/studio/favicon-192.png
deleted file mode 100644
index 1323376b82..0000000000
Binary files a/src/studio/favicon-192.png and /dev/null differ
diff --git a/src/studio/favicon-512.png b/src/studio/favicon-512.png
deleted file mode 100644
index 6320b50010..0000000000
Binary files a/src/studio/favicon-512.png and /dev/null differ
diff --git a/src/studio/head/NextStudioHead.tsx b/src/studio/head/NextStudioHead.tsx
new file mode 100644
index 0000000000..327726502a
--- /dev/null
+++ b/src/studio/head/NextStudioHead.tsx
@@ -0,0 +1,102 @@
+import faviconPng from './apple-touch-icon.png'
+import faviconIco from './favicon.ico'
+import faviconSvg from './favicon.svg'
+
+/** @public */
+export interface NextStudioHeadProps {
+ /**
+ * Sets the viewport to `viewport-fit=cover` to integrate with iOS devices with display cutouts (The Notch, Dynamic Island).
+ * Also sets `width=device-width, initial-scale=1` to make the studio page responsive.
+ * @defaultValue true
+ */
+ viewport?: boolean
+ /**
+ * It's common practice to hide the address to your Sanity Studio from search engines by setting `robots` to `noindex`
+ * @defaultValue 'noindex'
+ */
+ robots?: false | string
+ /**
+ * @defaultValue 'same-origin'
+ */
+ referrer?: false | string
+ /**
+ * Adds the same favicons as the `npx sanity dev` pipeline.
+ * @defaultValue true
+ */
+ favicons?: boolean
+ /**
+ * @defaultValue 'Sanity'
+ */
+ title?: false | string
+}
+
+/**
+ * In Next 13 appDir mode (`/app/studio/[[...index]]/head.tsx`):
+ * ```tsx
+ * // If you don't want to change any defaults you can just re-export the head component directly:
+ * export {NextStudioHead as default} from 'next-sanity/studio/head'
+ *
+ * // To customize it, use it as a children component:
+ * import {NextStudioHead} from 'next-sanity/studio/head'
+ *
+ * export default function CustomStudioHead() {
+ * return (
+ * <>
+ *
+ *
+ * >
+ * )
+ * }
+ * ```
+ * If you're using Next 12 or the `pages` folder (`/pages/studio/[[...index]].tsx`):
+ * ```tsx
+ * import Head from 'next/head'
+ * import {NextStudio} from 'next-sanity/studio'
+ * import {NextStudioHead} from 'next-sanity/studio/head'
+ *
+ * export default function StudioPage() {
+ * return (
+ * <>
+ *
+ *
+ *
+ *
+ * >
+ * )
+ * }
+ * ```
+ * @public
+ */
+export function NextStudioHead(props: NextStudioHeadProps) {
+ const {
+ viewport = true,
+ robots = 'noindex',
+ referrer = 'same-origin',
+ favicons = true,
+ title = 'Sanity',
+ } = props
+
+ return (
+ <>
+ {viewport && (
+
+ )}
+ {robots && }
+ {referrer && }
+ {title && {title} }
+ {favicons && }
+ {favicons && }
+ {favicons && }
+ >
+ )
+}
diff --git a/src/studio/apple-touch-icon.png b/src/studio/head/apple-touch-icon.png
similarity index 100%
rename from src/studio/apple-touch-icon.png
rename to src/studio/head/apple-touch-icon.png
diff --git a/src/studio/favicon.ico b/src/studio/head/favicon.ico
similarity index 100%
rename from src/studio/favicon.ico
rename to src/studio/head/favicon.ico
diff --git a/src/studio/favicon.svg b/src/studio/head/favicon.svg
similarity index 100%
rename from src/studio/favicon.svg
rename to src/studio/head/favicon.svg
diff --git a/src/studio/head/index.ts b/src/studio/head/index.ts
new file mode 100644
index 0000000000..8615d63cff
--- /dev/null
+++ b/src/studio/head/index.ts
@@ -0,0 +1 @@
+export * from './NextStudioHead'
diff --git a/src/studio/index.ts b/src/studio/index.ts
index 3daa05faf5..e6cc968c47 100644
--- a/src/studio/index.ts
+++ b/src/studio/index.ts
@@ -1,6 +1,7 @@
export * from './NextStudio'
-export * from './NextStudioGlobalStyle'
-export * from './NextStudioHead'
+export * from './NextStudioFallback'
+export * from './NextStudioLayout'
export * from './NextStudioNoScript'
-export * from './ServerStyleSheetDocument'
-export * from './utils'
+export * from './NextStudioSuspense'
+export * from './usePrefersColorScheme'
+export * from './useTheme'
diff --git a/src/studio/usePrefersColorScheme.ts b/src/studio/usePrefersColorScheme.ts
new file mode 100644
index 0000000000..ce109ceef4
--- /dev/null
+++ b/src/studio/usePrefersColorScheme.ts
@@ -0,0 +1,30 @@
+/* eslint-disable @typescript-eslint/no-empty-function */
+import type {ThemeColorSchemeKey} from '@sanity/ui'
+import {useSyncExternalStore} from 'react'
+
+function createStore() {
+ if (typeof document === 'undefined') {
+ return {
+ subscribe: () => () => {},
+ getSnapshot: () => 'light' as const,
+ getServerSnapshot: () => 'light' as const,
+ }
+ }
+
+ const matchMedia = window.matchMedia('(prefers-color-scheme: dark)')
+
+ return {
+ subscribe: (onStoreChange: () => void) => {
+ matchMedia.addEventListener('change', onStoreChange)
+ return () => matchMedia.removeEventListener('change', onStoreChange)
+ },
+ getSnapshot: () => (matchMedia.matches ? 'dark' : 'light'),
+ getServerSnapshot: () => 'light' as const,
+ }
+}
+const store = createStore()
+
+/** @alpha */
+export function usePrefersColorScheme(): ThemeColorSchemeKey {
+ return useSyncExternalStore(store.subscribe, store.getSnapshot, store.getServerSnapshot)
+}
diff --git a/src/studio/useTheme.ts b/src/studio/useTheme.ts
new file mode 100644
index 0000000000..ed15bdab72
--- /dev/null
+++ b/src/studio/useTheme.ts
@@ -0,0 +1,36 @@
+import {useMemo} from 'react'
+import {
+ type Config,
+ type SingleWorkspace,
+ type StudioTheme,
+ type WorkspaceOptions,
+ defaultTheme,
+} from 'sanity'
+
+type WithTheme = {
+ theme: StudioTheme
+}
+type SingleWorkspaceWithTheme = Omit & WithTheme
+type WorkspaceOptionsWithTheme = Omit & WithTheme
+
+function isWorkspaces(config: Config): config is WorkspaceOptions[] {
+ return Array.isArray(config)
+}
+
+function isWorkspaceWithTheme(
+ workspace: SingleWorkspace | WorkspaceOptions
+): workspace is SingleWorkspaceWithTheme | WorkspaceOptionsWithTheme {
+ return Boolean(workspace.theme)
+}
+
+/** @alpha */
+export function useTheme(config: Config): StudioTheme {
+ const workspace = useMemo(
+ () => (isWorkspaces(config) ? config[0] : config),
+ [config]
+ )
+ return useMemo(
+ () => (isWorkspaceWithTheme(workspace) ? workspace.theme : defaultTheme),
+ [workspace]
+ )
+}
diff --git a/src/studio/utils.ts b/src/studio/utils.ts
deleted file mode 100644
index 8fc80d6ba4..0000000000
--- a/src/studio/utils.ts
+++ /dev/null
@@ -1,105 +0,0 @@
-import {useRouter} from 'next/router'
-import {useMemo} from 'react'
-import {
- type Config,
- type SingleWorkspace,
- type StudioTheme,
- type WorkspaceOptions,
- defaultTheme,
-} from 'sanity'
-
-/** @alpha */
-export type WithTheme = {
- theme: StudioTheme
-}
-/** @alpha */
-export type SingleWorkspaceWithTheme = Omit & WithTheme
-/** @alpha */
-export type WorkspaceOptionsWithTheme = Omit & WithTheme
-/** @alpha */
-export type ConfigWithTheme = SingleWorkspaceWithTheme | WorkspaceOptionsWithTheme[]
-
-/** @alpha */
-export function isWorkspaces(config: Config): config is WorkspaceOptions[] {
- return Array.isArray(config)
-}
-
-/** @alpha */
-export function isWorkspaceWithTheme(
- workspace: SingleWorkspace | WorkspaceOptions
-): workspace is SingleWorkspaceWithTheme | WorkspaceOptionsWithTheme {
- return Boolean(workspace.theme)
-}
-
-/** @alpha */
-export function useTheme(config: Config): StudioTheme {
- const workspace = useMemo(
- () => (isWorkspaces(config) ? config[0] : config),
- [config]
- )
- return useMemo(
- () => (isWorkspaceWithTheme(workspace) ? workspace.theme : defaultTheme),
- [workspace]
- )
-}
-
-/** @alpha */
-export type MetaThemeColors = {
- themeColorLight: string
- themeColorDark: string
-}
-/** @alpha */
-export const useBackgroundColorsFromTheme = (theme: StudioTheme): MetaThemeColors => {
- return useMemo(
- () => ({
- themeColorLight: theme.color.light.default.base.bg,
- themeColorDark: theme.color.dark.default.base.bg,
- }),
- [theme]
- )
-}
-
-/** @alpha */
-export const useTextFontFamilyFromTheme = (theme: StudioTheme): string => {
- return useMemo(() => theme.fonts.text.family, [theme])
-}
-
-/**
- * Parses the next route to determine the what the base path for Sanity Studio should be
- * @alpha
- */
-export function useBasePath(): string {
- const router = useRouter()
- return useMemo(() => {
- const [basePath = '/'] = router.route.split('/[')
- return basePath
- }, [router.route])
-}
-
-/** @alpha */
-export interface WorkspaceWithBasePath extends Omit {
- basePath: string
-}
-/** @alpha */
-export type SingleWorkspaceWithBasePath = Omit & {basePath: string}
-/** @alpha */
-export type ConfigWithBasePath = SingleWorkspaceWithBasePath | WorkspaceOptions[]
-/**
- * Apply the base path from next to the config, prefixing any defined base path
- * @alpha
- */
-export function useConfigWithBasePath(config: Config): ConfigWithBasePath {
- const basePath = useBasePath()
- return useMemo(() => {
- if (isWorkspaces(config)) {
- return config.map((workspace) => ({
- ...workspace,
- basePath: workspace.basePath === '/' ? basePath : `${basePath}${workspace.basePath}`,
- }))
- }
- return {
- ...config,
- basePath: config.basePath === '/' ? basePath : `${basePath}${config.basePath || ''}`,
- }
- }, [config, basePath])
-}
diff --git a/src/studio/webmanifest.json b/src/studio/webmanifest.json
deleted file mode 100644
index b55b317f4c..0000000000
--- a/src/studio/webmanifest.json
+++ /dev/null
@@ -1,14 +0,0 @@
-{
- "icons": [
- {
- "src": "./favicon-192.png",
- "type": "image/png",
- "sizes": "192x192"
- },
- {
- "src": "./favicon-512.png",
- "type": "image/png",
- "sizes": "512x512"
- }
- ]
-}
diff --git a/studio.mjs b/studio.mjs
index b6f027f7fb..0c12b26379 100644
--- a/studio.mjs
+++ b/studio.mjs
@@ -1,33 +1,23 @@
-// Copied into dist/ by the build process, to workaround `next/head` not being available to native Nodejs ESM
+// Copied into dist/studio/ by the build process, to workaround `@sanity/icons` and other packages not using "type": "module"
-import studioCjs from './studio.cjs'
+import studioCjs from './index.cjs'
const {
- isWorkspaces,
- isWorkspaceWithTheme,
NextStudio,
- NextStudioGlobalStyle,
- NextStudioHead,
+ NextStudioFallback,
+ NextStudioLayout,
NextStudioNoScript,
- ServerStyleSheetDocument,
- useBackgroundColorsFromTheme,
- useBasePath,
- useConfigWithBasePath,
- useTextFontFamilyFromTheme,
+ NextStudioSuspense,
+ usePrefersColorScheme,
useTheme,
} = studioCjs
export {
- isWorkspaces,
- isWorkspaceWithTheme,
NextStudio,
- NextStudioGlobalStyle,
- NextStudioHead,
+ NextStudioFallback,
+ NextStudioLayout,
NextStudioNoScript,
- ServerStyleSheetDocument,
- useBackgroundColorsFromTheme,
- useBasePath,
- useConfigWithBasePath,
- useTextFontFamilyFromTheme,
+ NextStudioSuspense,
+ usePrefersColorScheme,
useTheme,
}
diff --git a/test.cjs b/test.cjs
index 6844b23378..cb7cf6df2a 100644
--- a/test.cjs
+++ b/test.cjs
@@ -19,31 +19,26 @@ assert.equal(typeof PreviewSuspense, 'function')
const nextSanityStudio = require('next-sanity/studio')
const {
NextStudio,
- NextStudioGlobalStyle,
- NextStudioHead,
+ NextStudioFallback,
+ NextStudioLayout,
NextStudioNoScript,
- ServerStyleSheetDocument,
- isWorkspaceWithTheme,
- isWorkspaces,
- useBackgroundColorsFromTheme,
- useBasePath,
- useConfigWithBasePath,
- useTextFontFamilyFromTheme,
+ NextStudioSuspense,
+ usePrefersColorScheme,
useTheme,
} = nextSanityStudio
assert.equal(typeof NextStudio?.type, 'function')
-assert.equal(typeof NextStudioGlobalStyle?.type, 'function')
-assert.equal(typeof NextStudioHead?.type, 'function')
+assert.equal(typeof NextStudioFallback?.type, 'function')
+assert.equal(typeof NextStudioLayout?.type, 'function')
assert.equal(typeof NextStudioNoScript, 'function')
-assert.equal(typeof ServerStyleSheetDocument, 'function')
-assert.equal(typeof isWorkspaceWithTheme, 'function')
-assert.equal(typeof isWorkspaces, 'function')
-assert.equal(typeof useBackgroundColorsFromTheme, 'function')
-assert.equal(typeof useBasePath, 'function')
-assert.equal(typeof useConfigWithBasePath, 'function')
-assert.equal(typeof useTextFontFamilyFromTheme, 'function')
+assert.equal(typeof NextStudioSuspense, 'function')
+assert.equal(typeof usePrefersColorScheme, 'function')
assert.equal(typeof useTheme, 'function')
+// Testing pkg.exports[./studio/head]
+const nextSanityStudioHead = require('next-sanity/studio/head')
+const {NextStudioHead} = nextSanityStudioHead
+assert.equal(typeof NextStudioHead, 'function')
+
// Testing pkg.exports[./webhook]
const nextSanityWebhook = require('next-sanity/webhook')
const {config, parseBody} = nextSanityWebhook
diff --git a/test.mjs b/test.mjs
index 7d3f4dc759..4c55bfa76a 100644
--- a/test.mjs
+++ b/test.mjs
@@ -16,33 +16,27 @@ assert.equal(typeof PreviewSuspense, 'function')
// Testing pkg.exports[./studio]
import {
- isWorkspaces,
- isWorkspaceWithTheme,
NextStudio,
- NextStudioGlobalStyle,
- NextStudioHead,
+ NextStudioFallback,
+ NextStudioLayout,
NextStudioNoScript,
- ServerStyleSheetDocument,
- useBackgroundColorsFromTheme,
- useBasePath,
- useConfigWithBasePath,
- useTextFontFamilyFromTheme,
+ NextStudioSuspense,
+ usePrefersColorScheme,
useTheme,
} from 'next-sanity/studio'
assert.equal(typeof NextStudio?.type, 'function')
-assert.equal(typeof NextStudioGlobalStyle?.type, 'function')
-assert.equal(typeof NextStudioHead?.type, 'function')
+assert.equal(typeof NextStudioFallback?.type, 'function')
+assert.equal(typeof NextStudioLayout?.type, 'function')
assert.equal(typeof NextStudioNoScript, 'function')
-assert.equal(typeof ServerStyleSheetDocument, 'function')
-assert.equal(typeof isWorkspaceWithTheme, 'function')
-assert.equal(typeof isWorkspaces, 'function')
-assert.equal(typeof useBackgroundColorsFromTheme, 'function')
-assert.equal(typeof useBasePath, 'function')
-assert.equal(typeof useConfigWithBasePath, 'function')
-assert.equal(typeof useTextFontFamilyFromTheme, 'function')
+assert.equal(typeof NextStudioSuspense, 'function')
+assert.equal(typeof usePrefersColorScheme, 'function')
assert.equal(typeof useTheme, 'function')
+// Testing pkg.exports[./studio/head]
+import {NextStudioHead} from 'next-sanity/studio/head'
+assert.equal(typeof NextStudioHead, 'function')
+
// Testing pkg.exports[./webhook]
import {config, parseBody} from 'next-sanity/webhook'
assert.equal(typeof config, 'object')