Remix > Setting up a preview mode

    Setting up a preview mode

    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 query a different GraphQL endpoint (the Preview endpoint) that returns content that is not yet published.

    Step 1: Create API routes to turn Preview mode on/off

    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:

    import { createCookieSessionStorage } from "remix";
    const { getSession, commitSession, destroySession } =
    createCookieSessionStorage({
    cookie: {
    name: "__session",
    maxAge: 604_800,
    path: '/',
    }
    });
    export { getSession, commitSession, destroySession };

    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:

    import { redirect } from 'remix';
    import { getSession, commitSession } from '~/sessions';
    export const action = async ({ request }) => {
    const session = await getSession(request.headers.get('Cookie'));
    session.set('preview', 'yes');
    return redirect('/', {
    headers: {
    'Set-Cookie': await commitSession(session),
    },
    });
    };

    Similarly, we also need to create a route under app/routes/preview/stop.js, to turn preview mode off:

    import { redirect } from 'remix';
    import { getSession, commitSession } from '~/sessions';
    export const action = async ({ request }) => {
    const session = await getSession(request.headers.get('Cookie'));
    session.unset('preview');
    return redirect('/', {
    headers: {
    'Set-Cookie': await commitSession(session),
    },
    });
    };

    We can now tweak the app/root.jsx file to add to every page a button to toggle the preview on and off:

    import { getSession } from '~/sessions';
    export const loader = async ({ request }) => {
    const session = await getSession(request.headers.get('Cookie'));
    return { previewEnabled: session.has('preview') };
    };
    export default function App() {
    const { previewEnabled } = useLoaderData();
    return (
    <html lang="en">
    <head>
    <meta charSet="utf-8" />
    <meta name="viewport" content="width=device-width,initial-scale=1" />
    <Meta />
    <Links />
    </head>
    <body>
    {previewEnabled ? (
    <Form method="post" action="/preview/stop">
    <button>Exit preview mode</button>
    </Form>
    ) : (
    <Form method="post" action="/preview/start">
    <button>Enter preview mode</button>
    </Form>
    )}
    <Outlet />
    <ScrollRestoration />
    <Scripts />
    {process.env.NODE_ENV === 'development' && <LiveReload />}
    </body>
    </html>
    );
    }

    Step 2: Fetch content from the Preview endpoint

    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 on a different GraphQL endpoint (the Preview endpoint) which returns the records at their latest version available instead of the one that's currently set as published:

    import { useLoaderData } from "remix";
    import { load } from "~/lib/datocms";
    const HOMEPAGE_QUERY = `query HomePage($limit: IntType) {
    posts: allBlogPosts(first: $limit) {
    title
    }
    }`;
    export async function loader({ request }) => {
    const session = await getSession(request.headers.get('Cookie'));
    return load({
    query: HOMEPAGE_QUERY,
    variables: { limit: 10 }
    preview: session.has('preview'),
    });
    };
    export default function Home() {
    const { posts } = useLoaderData();
    return <div>{JSON.stringify(posts, null, 2)}</div>;
    }