# Real-time updates

Live updates can be extremely useful both for content editors and the regular visitors of your app/website:

-   Content-editors in Preview Mode can **see drafts directly in the production website**, without having to refresh the page;
-   Visitors can **immediately see new content as it gets published**, allowing all kinds of real-time interactions with your website/app (ie. live-news coverage).
    

(Video content)

Inside a React Router project, it's extremely easy to use our [Real-time Updates API](https://www.datocms.com/docs/real-time-updates-api.md) to perform such changes, as it only involves adding a React hook to your page components.

### How to use the `useQuerySubscription` hook

The [`react-datocms`](https://github.com/datocms/react-datocms#live-real-time-updates) package exposes a [`useQuerySubscription` hook](https://github.com/datocms/react-datocms#live-real-time-updates) that makes it trivial to update any React Router page in real-time.

The hook works by streaming any changes present to the response of a GraphQL query directly to the browser, and it a `loader` responsibility to prepare an object compatible with the options of the hook itself.

The following code shows a complete example that **activates real-time updates for any visitor** of your website:

app/routes/blog-post.tsx

```tsx
import type { Route } from './+types/blog-post';
import { useQuerySubscription } from 'react-datocms';
import { load } from '~/lib/datocms';

const BLOG_POST_QUERY = `query HomePage {
  blogPost {
    title
  }
}`;

export async function loader() {
  return {
    subscription: {
      query: BLOG_POST_QUERY,
      initialData: await load(BLOG_POST_QUERY),
      token: process.env.DATOCMS_READONLY_TOKEN,
      environment: process.env.DATOCMS_ENVIRONMENT,
    },
  };
}

export default function BlogPost({ loaderData }: Route.ComponentProps) {
  const { data, error, status } = useQuerySubscription(loaderData.subscription);

  const statusMessage = {
    connecting: 'Connecting to DatoCMS...',
    connected: 'Connected to DatoCMS, receiving live updates!',
    closed: 'Connection closed',
  };

  return (
    <div>
      <p>Connection status: {statusMessage[status]}</p>
      {error && (
        <div>
          <h1>Error: {error.code}</h1>
          <div>{error.message}</div>
          {error.response && (
            <pre>{JSON.stringify(error.response, null, 2)}</pre>
          )}
        </div>
      )}
      {data && (
        <div>{JSON.stringify(data, null, 2)}</div>
      )}
    </div>
  );
}
```

### Preview Mode + `useQuerySubscription`

Another common scenario is being able to activate real-time updates of draft content **only for content editors** that are signed-in to the website via [Preview Mode](https://www.datocms.com/docs/react-router/setting-up-a-preview-mode.md):

(Video content)

In this case, you don't want to expose your API token or pass down additional arguments to regular users, so:

-   Make sure to pass the `includeDrafts: true` option only if Preview Mode is active (that is, if the `preview` key is set on the session), so that only content editors [will see draft content;](https://www.datocms.com/docs/content-delivery-api/api-endpoints.md)
-   If Preview Mode is off, fill in the `subscription` object returned by the loader with just `initialData` and `enabled: false` options, without any additional clutter.
    

Here's an example snippet:

app/routes/blog-post.tsx

```tsx
import type { Route } from './+types/blog-post';
import { load } from '~/lib/datocms';
import { getSession } from '~/sessions';

const BLOG_POST_QUERY = `query HomePage {
  blogPost {
    title
  }
}`;

export async function loader({ request }: Route.LoaderArgs) {
  const session = await getSession(request.headers.get('Cookie'));
  const previewModeActive = session.has('preview');

  const initialData = await load(BLOG_POST_QUERY, {
    includeDrafts: previewModeActive,
  });

  return {
    subscription: previewModeActive
      ? {
          query: BLOG_POST_QUERY,
          initialData,
          token: process.env.DATOCMS_READONLY_TOKEN,
          environment: process.env.DATOCMS_ENVIRONMENT,
          includeDrafts: true,
        }
      : {
          enabled: false,
          initialData,
        },
  };
}
```

## Related content in "React Router"

- [React Router + DatoCMS Overview](https://www.datocms.com/docs/react-router.md)
- [Managing images](https://www.datocms.com/docs/react-router/managing-images.md)
- [Displaying videos](https://www.datocms.com/docs/react-router/displaying-videos.md)
- [Structured Text fields](https://www.datocms.com/docs/react-router/structured-text-fields.md)
- [Adding SEO to pages](https://www.datocms.com/docs/react-router/seo-management.md)
- [Setting up a preview mode](https://www.datocms.com/docs/react-router/setting-up-a-preview-mode.md)
- [Real-time updates](https://www.datocms.com/docs/react-router/real-time-updates.md)