Web Preview plugin

I’m using your web preview plugin with remix. In my entry point I get the two link generated as expected. but in the plugin I get this error:

Webhook error: check the console for more info!

I’m currently send this back from my entrypoint:

const previewLinks = [
      label: 'Published version',
      url: `${baseUrl}${url}`,
      label: 'Draft version',
      url: `${baseUrl}/preview/start?redirect=${url}&secret=${process.env.PREVIEW_MODE_SECRET}`,

  return new Response(JSON.stringify({ previewLinks }), {
    status: 200,
    headers: {
      'Access-Control-Allow-Origin': '*',
      'Access-Control-Allow-Methods': 'POST, OPTIONS',
      'Access-Control-Allow-Headers': 'Content-Type, Authorization',
      'Content-Type': 'application/json',

Any idea how to fix it?

1 Like

+1 on this. I have the same issue. I even tried to create a custom webhook sharing the same endpoint and headers that worked just fine.

Hi @elia and @felix.proulx,

Thanks for reporting this! I’m a Dato support agent and I’ll do my best to troubleshoot this with you.

I’m trying to reproduce this locally, but could you please help me by providing a bit more information?

  • Where are you hosting this function? (Vercel, Netlify, somewhere else?)
  • How are you generating the response for your entry point? Is it a serverless function, or?
  • Can you please provide the rest of the code, e.g. where are you getting baseUrl and url in your example?
  • What is the error in your Javascript console when you try to open the web preview? Are you seeing a CORS error or something else?

And @felix.proulx , if your answers are any different, please feel to provide them here too :slight_smile:

Thank you!

Hmm… I’m wondering if this situation might be partially due to a mismatch between our Remix starter project (which uses an older, cookie-based preview system) vs the Web Previews plugin (which uses a newer webhooks and redirect based system)? Once we figure out what’s going on here, I’ll make sure to update the project and documentation so it’s clearer.

In the meantime, after some work, I was able to get a Remix project working with the plugin, but it was quite the hassle.

Can we see if these steps line up with what you’ve tried already?

Requirement 1: Webhook destination serverless func / handler

You’ll need to set up a new route in Remix to handle the webhook. This is the serverless func that will catch your outgoing webhook (from the plugin) and return the two URLs.

Example file here (sorry for the jank, it’s my first time working with Remix).

Some caveats, though:

  • It needs a loader function to handle the webhook’s OPTIONS request and return the proper CORS headers
  • It ALSO needs a separate action function that handles the webhook’s actual POST request. This function does several things:
    • Figure out the current domain name the site is hosted at (creating a baseUrl, in my case in the example from Vercel or Netlify’s env vars)
    • Parse the incoming webhook’s JSON (await request.json()) to get the webhook’s body, which DatoCMS fills with dynamic data matching whatever entry you called the plugin from
    • Using this request body, look up & return the proper URLs based on rules you define. In my example, it examines the request’s item type (via the itemType.attributes.api_key). And if it’s a post, it returns a URL like posts/$slug

Requirement 2: Some way to start and stop the preview system, and redirect to published or draft versions of a page

IMO this is one of the more confusing parts. The plugin shows a Next.js example implementation, and that works fine with our Next.js starter app because it’s built to handle the redirect: https://github.com/datocms/nextjs-demo/blob/master/app/api/draft/route.js#L20-L26

However, our Remix starter project is NOT currently set up to do that. By default, its start/stop systems only set a cookie and then redirect back to the home page. With a little bit of work, you can modify them to work with the plugin:

In both cases, I added a new loader function that is largely similar to the existing action function, with a few significant differences:

  • They are a loader because that’s what Remix uses to handle GET requests from your browser (like when you click the preview link from the sidebar). The original action can only handle POST requests, which is what the starter project normally uses to set a cookie, but the plugin uses a different setup.
  • They also have to parse the request URL for the redirect URL parameter, and redirect your browser to that page instead of back to the home page
  • This still have to set/unset the preview cookie, because that’s what our Remix starter project uses to handle preview mode. (The Next.js project functions differently)

That’s quite a handful, isn’t it? But doing the above at least let me get a working deployment up for the preview links in the sidebar… (though not yet the side-by-side preview; will have to work on that later).

Does referencing that help at all vs what you’ve tried so far?

If not, please do provide more details and we’ll try to figure it out!

Hi roger,

To be honest I switched to a sidebar button plugin for now so I could deliver something that works in my project timeline. You can focus your energies on Elias case. I would still like to understand and maybe implement it in a future project.

For the preview I use GitHub - darbymanning/sveltekit-preview-mode: Easily enable preview mode for your CMS with SvelteKit and it’s working fine. I don’t think this was part of the problem. Correct me if I’m wrong but I figured any links returned in a properly formatted payload would at least be displayed the on Dato admin.


1 Like

Of course. Glad you found a workaround for now!

It sounds like your project is (/was) different enough that it might be worth troubleshooting separately? If you ever want to dive back into it, we’d be happy to look into it with you.

If that time should arise, please feel free to start a new thread on it and provide whatever details you can (any code snippets or specific error messages, especially). You can post that whenever you like and we can work together on it async, so that when it’s ready, you can backport it to your client’s project on your timeline?

Sorry about the inconvenience!

Hey @roger,
we figure out to make it works in the same way you write in the post.
it was missing the header sent in the loader, in the response of the OPTIONS request.



1 Like

Great, thank you for confirming!