Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 0 additions & 28 deletions app/(pages)/(sanity)/layout.tsx

This file was deleted.

22 changes: 19 additions & 3 deletions app/api/draft-mode/enable/route.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,23 @@
import { NextResponse } from 'next/server'
import { defineEnableDraftMode } from 'next-sanity/draft-mode'
import { client } from '~/integrations/sanity/client'
import { privateToken } from '~/integrations/sanity/env'

export const { GET } = defineEnableDraftMode({
client: client.withConfig({ token: privateToken }),
})
function getEnableDraftMode() {
if (!(client && privateToken)) {
console.error('Sanity is not configured')
return {
GET: () =>
NextResponse.json(
{ error: 'Sanity is not configured' },
{ status: 500 }
),
}
}

return defineEnableDraftMode({
client: client.withConfig({ token: privateToken }),
})
}

export const { GET } = getEnableDraftMode()
15 changes: 14 additions & 1 deletion app/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,13 @@ import AppData from '~/package.json'
import { themes } from '~/styles/colors'
import '~/styles/css/index.css'

import { draftMode } from 'next/headers'
import Script from 'next/script'
import { VisualEditing } from 'next-sanity/visual-editing'
import { GSAPRuntime } from '~/components/gsap/runtime'

import { isSanityConfigured } from '~/integrations/check-integration'
import { DisableDraftMode } from '~/integrations/sanity/components/disable-draft-mode'
import { SanityLive } from '~/integrations/sanity/live'
import { OrchestraTools } from '~/orchestra'
import { fontsVariable } from '~/styles/fonts'

Expand Down Expand Up @@ -82,6 +86,7 @@ export const viewport: Viewport = {
}

export default async function Layout({ children }: PropsWithChildren) {
const { isEnabled: isDraftMode } = await draftMode()
return (
<html
lang="en"
Expand Down Expand Up @@ -109,6 +114,14 @@ export default async function Layout({ children }: PropsWithChildren) {
// patch={!isDraftMode}
patch={true}
/>

{isSanityConfigured() && isDraftMode && (
<>
<VisualEditing />
<DisableDraftMode />
<SanityLive />
</>
)}
</body>
</html>
)
Expand Down
5 changes: 5 additions & 0 deletions app/sitemap.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,11 @@ export default async function sitemap(): Promise<MetadataRoute.Sitemap> {
if (isSanityConfigured()) {
try {
const { client } = await import('~/integrations/sanity')

if (!client) {
return baseRoutes
}

const { groq } = await import('next-sanity')

// Fetch all published pages and articles
Expand Down
1,014 changes: 687 additions & 327 deletions bun.lock

Large diffs are not rendered by default.

49 changes: 32 additions & 17 deletions integrations/sanity/client.ts
Original file line number Diff line number Diff line change
@@ -1,21 +1,36 @@
import { createClient } from 'next-sanity'
import { isSanityConfigured } from '../check-integration'
import { apiVersion, dataset, privateToken, projectId, studioUrl } from './env'

export const client = createClient({
projectId,
dataset,
apiVersion,
useCdn: true,
perspective: 'published',
token: privateToken,
stega: {
studioUrl,
filter: (props) => {
if (props.sourcePath.at(-1) === 'title') {
return true
}
let _client: ReturnType<typeof createClient> | null = null

return props.filterDefault(props)
},
},
})
export function getClient() {
if (!isSanityConfigured()) {
console.error('Sanity is not configured')
return null
}

if (!_client) {
_client = createClient({
projectId,
dataset,
apiVersion,
useCdn: true,
perspective: 'published',
token: privateToken,
stega: {
studioUrl,
filter: (props) => {
if (props.sourcePath.at(-1) === 'title') {
return true
}
return props.filterDefault(props)
},
},
})
}

return _client
}

export const client = getClient()
3 changes: 2 additions & 1 deletion integrations/sanity/env.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,8 @@ export const previewURL = assertValue(

function assertValue<T>(v: T | undefined, errorMessage: string): T {
if (v === undefined) {
throw new Error(errorMessage)
console.error(errorMessage)
return undefined as T
}

return v
Expand Down
4 changes: 4 additions & 0 deletions integrations/sanity/fetch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,10 @@ export async function fetchSanity<T>(
): Promise<T> {
const signal = cacheSignal()

if (!client) {
throw new Error('Sanity is not configured')
}
Comment on lines +30 to +32
Copy link

Copilot AI Dec 9, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The error handling is inconsistent across the codebase. Here you throw an error, but in integrations/sanity/live/index.tsx:12-16 you return a resolved promise with an error object, and in app/api/draft-mode/enable/route.ts:11-15 you return a 500 response.

For consistency and better user experience, consider:

  1. Using the same error handling pattern across all fallback implementations
  2. Throwing errors early (like here) is good, but then the other fallbacks should also throw or handle errors consistently
  3. Document the expected behavior when Sanity is not configured

Copilot uses AI. Check for mistakes.

return client.fetch<T>(query, params, {
// Only pass signal if cacheSignal returns a non-null value
// Cast to AbortSignal for type compatibility
Expand Down
39 changes: 34 additions & 5 deletions integrations/sanity/live/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,37 @@ import { defineLive } from 'next-sanity/live'
import { client } from '../client'
import { privateToken, publicToken } from '../env'

export const { sanityFetch, SanityLive } = defineLive({
client,
browserToken: publicToken,
serverToken: privateToken,
})
let _sanityLive: ReturnType<typeof defineLive> | null = null

function getSanityLive() {
if (!client) {
console.error('Sanity is not configured')
return null
}

if (!_sanityLive) {
_sanityLive = defineLive({
client,
browserToken: publicToken,
serverToken: privateToken,
Comment on lines +16 to +17
Copy link

Copilot AI Dec 9, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When client is truthy but publicToken or privateToken are undefined (due to the modified assertValue function), defineLive will be called with undefined tokens. This could cause runtime errors or unexpected behavior in the Sanity live query functionality.

Consider adding explicit checks for required tokens:

client && publicToken && privateToken
  ? defineLive({...})
  : {...}

Copilot uses AI. Check for mistakes.
})
}

return _sanityLive
}

const sanityLiveInstance = getSanityLive()

type SanityLiveInstance = NonNullable<typeof sanityLiveInstance>
type SanityFetchFunction = SanityLiveInstance['sanityFetch']

export const sanityFetch: SanityFetchFunction = ((...args) => {
if (!sanityLiveInstance?.sanityFetch) {
throw new Error(
'Sanity is not configured. Please check your environment variables.'
)
}
return sanityLiveInstance.sanityFetch(...args)
}) as SanityFetchFunction

export const SanityLive = sanityLiveInstance?.SanityLive || (() => null)