Corey Ward
Freelance full-stack dev focused on building awesome Jamstack experiences
Drop this into your Studio to let editors know when there's a more recent version of your Studio available, making sure they have the latest fields and validations.
import {useState, useEffect, useRef} from 'react'
import {type LayoutProps, isDev} from 'sanity'
import {Flex, Text, Button, Dialog, Box} from '@sanity/ui'
function reload() {
window.location.reload()
}
const CHECK_INTERVAL = 30000
function getBundleHash(html?: string): string | null {
const scripts = html
? new DOMParser().parseFromString(html, 'text/html').querySelectorAll('script[src]')
: document.querySelectorAll('script[src]')
for (const script of scripts) {
const src = script.getAttribute('src')
if (src?.includes('sanity-') && src.endsWith('.js')) {
const match = src.match(/sanity-([a-zA-Z0-9_-]+)\.js/)
if (match?.[1]) {
return match[1]
}
}
}
return null
}
export const Layout = (props: LayoutProps) => {
const [showDialog, setShowDialog] = useState(false)
const initialHashRef = useRef<string | null>(null)
const checkIntervalRef = useRef<NodeJS.Timeout | null>(null)
useEffect(() => {
if (isDev) {
return
}
// Extract initial bundle hash on mount
const initialHash = getBundleHash()
initialHashRef.current = initialHash
// Set up interval to check for new version every 30 seconds
checkIntervalRef.current = setInterval(async () => {
try {
const response = await fetch(window.location.origin + window.location.pathname)
const html = await response.text()
const newHash = getBundleHash(html)
if (newHash && initialHashRef.current && newHash !== initialHashRef.current) {
setShowDialog(true)
}
} catch (error) {
// Silently handle errors (network failures, etc.)
console.error('Failed to check for new version:', error)
}
}, CHECK_INTERVAL)
return () => {
if (checkIntervalRef.current) {
clearInterval(checkIntervalRef.current)
}
}
}, [])
return (
<>
{showDialog && (
<Dialog
header="New version available"
id="dialog-example"
animate
onClose={() => {}}
footer={
<Flex width="full" gap={3} justify="flex-start" padding={4} align="center">
<Button
mode="default"
padding={2}
text="Reload"
tone="primary"
data-testid="confirm-button"
onClick={reload}
/>
</Flex>
}
zOffset={1000}
>
<Box padding={4}>
<Text>A new version of the Studio is available. Please reload to update.</Text>
</Box>
</Dialog>
)}
{props.renderDefault({...props})}
</>
)
}import {Layout} from './components/Layout'
export default defineConfig({
// rest of config
studio: {
components: {
layout: Layout,
},
},
})// Studio version 2 only
import { useEffect } from "react"
import config from "config:sanity"
const BUNDLE_CHECK_INTERVAL = 60 * 1000
const CHANGES_AVAILABLE_MESSAGE =
"New changes are available! For the best results the page will be refreshed to get the latest updates."
async function getCurrentHash() {
const basePath = (config.project && config.project.basePath) || "/"
const html = await window.fetch(basePath).then((res) => res.text())
const [, hash] = html.match(/app\.bundle\.js\?(\w+)/) || []
return hash
}
let hash = null
let interval = null
const BundleChecker = () => {
useEffect(() => {
getCurrentHash().then((newHash) => {
hash = newHash
})
interval = createInterval()
return () => clearInterval(interval)
}, [])
// We're a react component, in theory, so return null to not render anything
return null
}
export default BundleChecker
const createInterval = () =>
setInterval(async () => {
const newHash = await getCurrentHash()
if (hash && newHash !== hash) {
clearInterval(interval)
if (window.confirm(CHANGES_AVAILABLE_MESSAGE)) {
window.location.reload()
} else {
interval = createInterval()
}
}
}, BUNDLE_CHECK_INTERVAL)// Studio version 2 only
{
"parts": [
{
"implements": "part:@sanity/base/absolutes",
"path": "./bundleChecker.js"
},
]
}The Sanity Studio runs as a single-page app, so users don't get the latest version every time they change "pages". Instead, when you deploy changes to the Sanity Studio (e.g. when you run sanity deploy) your editors need to refresh their browser or they'll be working on an outdated version that might include different fields, validations, or types, creating inconsistent data that can lead to bugs.
Sanity Studio >=3
Add the the components/Layout.tsx file to your code repository, and configure your sanity.config.tsx to import this component and use it as your Studio layout component. This will check every 30 seconds (customize by changing the CHECK_INTERVAL variable) and show a dialog if a new Studio version is available.
Sanity Studio v2
Drop the contents of bundleChecker.js into your Studio and configure it to your sanity.json under the parts array and your Studio will now make periodic checks (set to once every 60 seconds by default) to see if there are any changes to your Studio code available. If so, it'll prompt the user to refresh.
Freelance full-stack dev focused on building awesome Jamstack experiences
Open-sourceror @ Sanity.io
Solutions Architect at Sanity
🚫 MyFile / ✅ MyFile.pdf
Go to Add extensions to asset original filenamesA code snippet to create a custom '+ Add item' button to add items to the top of an array.
Go to Add items to top of array with custom '+ Add item' buttonA script to validate that your schema is free of errors and warnings
Go to Validate schema scriptScript to find and delete unused assets in a dataset
Go to Delete unused assets