Remix Blog
Words are nice... but code speaks louder. Dive into a fully commented project template, showcasing these techniques (and more) in action.
Most often than not, editors of a DatoCMS project will find very beneficial to have a preview of how the changes they are making to ie. an article will be rendered inside the final website.
With Remix you can easily add a "preview mode" to your production website. With that, requests coming from editors will add a special header — X-Include-Drafts
— that returns content that is not yet published.
First, we need to create a couple of API routes to enable/disable Preview Mode. We're going to use Remix's built-in session management to store a cookie inside the browser of the visitor.
First step is to actually create the session manager. Create a new file under app/sessions.js
:
Now we can use it inside a new API route under app/routes/preview/start.js
, that we can call to turn on the preview mode:
1import { redirect } from 'remix';2import { getSession, commitSession } from '~/sessions';3
4export const action = async ({ request }) => {5 const session = await getSession(request.headers.get('Cookie'));6
7 session.set('preview', 'yes');8
9 return redirect('/', {10 headers: {11 'Set-Cookie': await commitSession(session),12 },13 });14};
Similarly, we also need to create a route under app/routes/preview/stop.js
, to turn preview mode off:
1import { redirect } from 'remix';2import { getSession, commitSession } from '~/sessions';3
4export const action = async ({ request }) => {5 const session = await getSession(request.headers.get('Cookie'));6
7 session.unset('preview');8
9 return redirect('/', {10 headers: {11 'Set-Cookie': await commitSession(session),12 },13 });14};
We can now tweak the app/root.jsx
file to add to every page a button to toggle the preview on and off:
1import { getSession } from '~/sessions';2
3export const loader = async ({ request }) => {4 const session = await getSession(request.headers.get('Cookie'));5 return { previewEnabled: session.has('preview') };6};7
8export default function App() {9 const { previewEnabled } = useLoaderData();10
11 return (12 <html lang="en">13 <head>14 <meta charSet="utf-8" />15 <meta name="viewport" content="width=device-width,initial-scale=1" />16 <Meta />17 <Links />18 </head>19 <body>20 {previewEnabled ? (21 <Form method="post" action="/preview/stop">22 <button>Exit preview mode</button>23 </Form>24 ) : (25 <Form method="post" action="/preview/start">26 <button>Enter preview mode</button>27 </Form>28 )}29 <Outlet />30 <ScrollRestoration />31 <Scripts />32 {process.env.NODE_ENV === 'development' && <LiveReload />}33 </body>34 </html>35 );36}
Now every loader
can know from the session if the visitor is currently in preview mode by looking at the request
object.
If that's the case, we can run the same query, but passing the X-Include-Drafts
header, which returns the records at their latest version available instead of the one that's currently set as published:
1import { useLoaderData } from "remix";2import { load } from "~/lib/datocms";3
4const HOMEPAGE_QUERY = `query HomePage($limit: IntType) {5 posts: allBlogPosts(first: $limit) {6 title7 }8}`;9
10export async function loader({ request }) => {11 const session = await getSession(request.headers.get('Cookie'));12
13 return load(HOMEPAGE_QUERY, {14 variables: { limit: 10 }15 includeDrafts: session.has('preview'),16 });17};18
19export default function Home() {20 const { posts } = useLoaderData();21
22 return <div>{JSON.stringify(posts, null, 2)}</div>;23}