How to setup draft mode with SvelteKit, Sanity.io and Vercel

Reading Time: 8 minutes

In this article we're going to look at achieving a very specific set of goals; setting up drafts with SvelteKit, Sanity and Vercel.

However, even if you're not necessarily hosting on Vercel, you may still be able to glean some useful information to get you going.

For this article, were going to assume that you are using TypeScript, if you're not, no worries, ignore the typing and assume the .ts files are to use a .js extension instead. We're also assuming that you will be running your site in ISR mode, if you're not, then you can skip the ISR config.

First, let's tackle Draft Mode.

Draft Mode

To configure Draft Mode using SvelteKit, Sanity and Vercel, we're going to need to do a few things:

  • Configure an Open preview url in Sanity.
  • Configure a Bypass Token in Vercel and in our local .env file.
  • Export ISR config in our +page.server.ts files.
  • Configure a Draft mode hook in the hooks.server.ts file.
  • Add a Draft Mode api route.
  • Add a Leave Draft Mode api route.
  • Update the vercel.json file
  • Fetch Posts from Sanity.io in Draft mode.
  • Adding a Leave Draft Mode toolbar.

Configuring an Open preview url in Sanity

Setting up a Preview URL in Sanity is fairly straight forward, we'll need to edit the sanity.config.ts file, located in the root of our sanity install. Inside the sanity config file,

Here we are limiting the Preview URL to documents that have a slug field configured called slug. You may need to adjust this depending on your use case.

We set the url to match the site base url, with /api/draft-mode as the path, we pass it the document slug and the secret as query parameters.

For the draftModeSecret and baseURL it would be advisable here to not keep them within code, you could store these in Sanity with a custom site settings singleton document and extract them. Here is a guide on how to create a singleton in Sanity.

Open preview in Sanity.io

We should now be able to see an Open preview url in our editor, but at the moment, it won't do anything, so let's fix that.

Configuring a bypass token

The following article from kit.svelte.dev explains how to set up ISR mode (incremental site regeneration) with SvelteKit and Vercel. The bypass token, when passed in as a cookie with the name x-prerender-revalidate will force a page to be re-validated, which will allow us to view an ISR page in Draft mode, so let's do that now.

Environment Variables In Vercel

Under the project settings for your site on Vercel, go to the Environment Variables tab and create a new token called BYPASS_TOKEN you can generate or create a random token here.

Add an environment variable called DRAFT_MODE_COOKIE and set the value to __prerender_bypass.

Add an environment variable called SITE_URL and set it to the absolute url of your site, this should match what you set earlier for the base url in your Sanity preview url config.

Also, in your .env.local file, add the same variables.

Configuring the ISR config

ISR is an excellent way of keeping your site performant, by rebuilding your site incrementally over time, but if you don't intend on using it, this setup should still work for you.

Let's assume you have a page called posts, inside your src/routes/posts/[...slug] directory create a +page.server.ts file if you don't already have one, and add the following:

For the expiration value, we recommend setting one up in vercel and your .env.local file, to keep track of it.

What the config above will do is specify what our bypass token will be, which will need to be present in the __prerender_bypass cookie in order for this to work, and will set an expiration of 1 minute, meaning the page will invalidate after 60 seconds have passed.

The expiration value should differ depending on your needs; you may choose a lower value for pages that should get updates more frequently, and higher values for pages that are unlikely to be changed in a long time.

Configuring a draft mode hook

First we need to add a new local variable, inside the src directory of your SvelteKit project, edit the app.d.ts file and add a new local:

Here we are defining the draftMode variable which we will use later.

Inside the src directory of you SvelteKit project, add the following:

Here we set the draftMode local variable based upon the presence of the draft mode cookie we set in our environment variables and whether it matches the value of the bypass token. If it does, then we will consider the site to be in draft mode.

Adding a Draft Mode api route

We need to now create a new route to handle the setting of the draft mode cookie, create the following file src/routes/app/api/draft-mode/+server.ts

Just like we set via our Sanity preview url earlier, we check the secret against the bypass token if the secret matches, we set the cookie, which should be __prerender_bypass to the value of our bypass token.

Adding a Leave Draft Mode api route

Now we need a way to exit draft mode, create the following file: src/routes/app/api/draft-mode/+server.ts

Here we simply delete the draft mode cookie and redirect back to the homepage.

Updating the Vercel.json file

Create or modify the vercel.json file at the root of your SvelteKit project.

Here we simply instruct Vercel not to cache our draft mode endpoints. You can learn more about configuring projects with vercel.json here.

Fetching posts from Sanity in Draft mode

Now that we have draft mode configured, we actually need to make sure we're fetching content. If you don't have a function to create the Sanity client, create one, or adjust it to match the following:

Also, if you haven't created a Sanity Token, create one and add a SANITY_TOKEN environment variable to vercel and your .env.local file. The following guide will direct you on how to set one up.

Let's test this out now on our post page edit the src/routes/posts/[...slug]/+page.server.ts file:

Here we'll fetch the post by slug and we're assuming all posts will have a blog/ prefix in their slug. We'll store the getPost() function inside of the src/lib/sanity.ts file, and we pass the draftMode parameter to it which we get from locals, that was configured earlier. Let's add the getPost() function to the following file src/lib/sanity.ts:

Here we use the getSanityClient() function from earlier and pass it our draftMode parameter, we then create a new function called getLatestDraft if we are in draft mode, let's add that now to the same file.

In this function, we'll filter out the items that have a drafts. id prefix and return the first result, if we don't have an active draft, we'll return the published version instead.

Adding a Leave Draft Mode toolbar

Leaving draft mode is as simple as deleting the cookie or heading to our leave draft mode route, but let's make that easier for us by adding a bar at the bottom of the page. For this, we're assuming you're using Tailwind, but you should be able to adapt this to your own requirements.

In our src/routes/+layout.server.ts file, add the following, or update your existing file:

This will pass our draftMode variable to the layout.

Now edit the src/routes/+layout.ts file:

If we're in draft mode, then we should see a black bar along the bottom of our page.

Draft Mode With SvelteKit and Sanity In Action

Fetching all posts for a post listing page

For most scenarios, you'll have to be creative in how you fetch and filter data in Sanity, for instance, what if you want to display posts on the blog overview page in draft mode, to see the teaser card preview? We can do this with the following function in our src/lib/sanity.ts file:

On demand revalidation

If you're wondering how to set up on demand revalidation with SvelteKit, Sanity and Vercel, check out our next article here.

Enjoyed this article? Please share it