diff --git a/examples/remix/app/components/preview-banner.tsx b/examples/remix/app/components/preview-banner.tsx
index 29ef9341..4e6567d2 100644
--- a/examples/remix/app/components/preview-banner.tsx
+++ b/examples/remix/app/components/preview-banner.tsx
@@ -2,11 +2,11 @@ import React from 'react';
export function PreviewBanner() {
return (
-
- You're in preview mode (DRAFT content from Contentful served)
+ <>
+
You're in preview mode (DRAFT content from Contentful served)
-
+ >
);
}
diff --git a/examples/remix/app/routes/$slug.tsx b/examples/remix/app/routes/$slug.tsx
index 35b47c9d..bb43538b 100644
--- a/examples/remix/app/routes/$slug.tsx
+++ b/examples/remix/app/routes/$slug.tsx
@@ -9,27 +9,9 @@ import {
} from '@contentful/live-preview/react';
import { PreviewBanner } from '../components/preview-banner';
-import { contentful } from '../../lib/contentful.server';
+import { getEntryBySlug } from '../../lib/contentful.server';
import { isPreviewMode } from '../utils/preview-mode.server';
-
-type QueryResponse = {
- postCollection: {
- items: Post[];
- };
-};
-
-type Post = {
- title: string;
- description: string;
- sys: {
- id: string;
- };
-};
-
-type LoaderData = {
- post: Post;
- preview: boolean;
-};
+import type { LoaderData } from '../../types';
const getPostQuery = gql`
query Post($slug: String!, $preview: Boolean!) {
@@ -49,20 +31,15 @@ const getPostQuery = gql`
export const loader: LoaderFunction = async ({ params, request }) => {
const { slug } = params;
-
const preview = await isPreviewMode(request);
-
- const API_TOKEN = preview
- ? process.env.CONTENTFUL_PREVIEW_ACCESS_TOKEN
- : process.env.CONTENTFUL_ACCESS_TOKEN;
-
- const data = (await contentful.request(
- getPostQuery,
- { slug, preview },
- { authorization: `Bearer ${API_TOKEN}` }
- )) as QueryResponse;
-
- const post = data.postCollection.items[0];
+ const data = slug && await getEntryBySlug({
+ spaceId: process.env.CONTENTFUL_SPACE_ID || '',
+ accessToken: preview ? process.env.CONTENTFUL_PREVIEW_ACCESS_TOKEN || '' : process.env.CONTENTFUL_ACCESS_TOKEN || '',
+ query: getPostQuery,
+ slug,
+ preview
+ });
+ const post = data && data.postCollection.items[0];
return json({
post,
@@ -72,14 +49,19 @@ export const loader: LoaderFunction = async ({ params, request }) => {
export default function PostDetailPage() {
const { post, preview } = useLoaderData();
- const inspectorProps = useContentfulInspectorMode({ entryId: post.sys.id });
+ const inspectorProps = useContentfulInspectorMode({ entryId: post?.sys.id });
const updatedPost = useContentfulLiveUpdates(post);
return (
<>
{preview && }
- {updatedPost.title || ''}
- {updatedPost.description || ''}
+
+ {post && (
+ <>
+ {updatedPost.title || ''}
+ {updatedPost.description || ''}
+ >
+ )}
>
);
}
diff --git a/examples/remix/app/routes/api/preview.ts b/examples/remix/app/routes/api/preview.ts
index e1d24dc6..5866f461 100644
--- a/examples/remix/app/routes/api/preview.ts
+++ b/examples/remix/app/routes/api/preview.ts
@@ -2,18 +2,10 @@ import { gql } from 'graphql-request';
import { json, redirect } from '@remix-run/node';
import type { LoaderFunction } from '@remix-run/node';
-import { contentful } from '../../../lib/contentful.server';
+import { getEntryBySlug } from '../../../lib/contentful.server';
import { previewModeCookie } from '../../utils/preview-mode.server';
import { parseCookie } from '../../utils/parse-cookie.server';
-type QueryResponse = {
- postCollection: {
- items: {
- slug: string;
- }[];
- };
-};
-
const getPostQuery = gql`
query Post($slug: String!) {
postCollection(where: { slug: $slug }, limit: 1, preview: true) {
@@ -35,13 +27,13 @@ export const loader: LoaderFunction = async ({ request }) => {
}
// Check if the provided `slug` exists
- const data = (await contentful.request(
- getPostQuery,
- { slug },
- {
- authorization: `Bearer ${process.env.CONTENTFUL_PREVIEW_ACCESS_TOKEN}`,
- }
- )) as QueryResponse;
+ const data = await getEntryBySlug({
+ spaceId: process.env.CONTENTFUL_SPACE_ID || '',
+ accessToken: process.env.CONTENTFUL_PREVIEW_ACCESS_TOKEN || '',
+ query: getPostQuery,
+ slug,
+ preview: true
+ });
// If the slug doesn't exist prevent preview from being enabled
if (!data.postCollection.items.length) {
diff --git a/examples/remix/app/routes/index.tsx b/examples/remix/app/routes/index.tsx
index 84a0414f..721fd491 100644
--- a/examples/remix/app/routes/index.tsx
+++ b/examples/remix/app/routes/index.tsx
@@ -4,26 +4,10 @@ import { json } from '@remix-run/node';
import { useLoaderData } from '@remix-run/react';
import { gql } from 'graphql-request';
-import { contentful } from '../../lib/contentful.server';
+import { getEntries } from '../../lib/contentful.server';
import { isPreviewMode } from '../utils/preview-mode.server';
import { PreviewBanner } from '../components/preview-banner';
-
-type QueryResponse = {
- postCollection: {
- items: Post[];
- };
-};
-
-type Post = {
- title: string;
- description: string;
- slug: string;
-};
-
-type LoaderData = {
- posts: Post[];
- preview: boolean;
-};
+import type { LoaderData, Post } from '../../types';
const getPostQuery = gql`
query Post($preview: Boolean!) {
@@ -44,18 +28,12 @@ const getPostQuery = gql`
export const loader: LoaderFunction = async ({ request }) => {
const preview = await isPreviewMode(request);
-
- const API_TOKEN = preview
- ? process.env.CONTENTFUL_PREVIEW_ACCESS_TOKEN
- : process.env.CONTENTFUL_ACCESS_TOKEN;
-
- const data = (await contentful.request(
- getPostQuery,
- { preview },
- {
- authorization: `Bearer ${API_TOKEN}`,
- }
- )) as QueryResponse;
+ const data = await getEntries({
+ spaceId: process.env.CONTENTFUL_SPACE_ID || '',
+ accessToken: preview ? process.env.CONTENTFUL_PREVIEW_ACCESS_TOKEN || '' : process.env.CONTENTFUL_ACCESS_TOKEN || '',
+ query: getPostQuery,
+ preview
+ })
return json({
posts: data.postCollection.items,
@@ -69,7 +47,7 @@ export default function Index() {
return (
<>
{preview && }
- {posts.map((post) => (
+ {posts && posts.map((post: Post) => (
{post.title}
diff --git a/examples/remix/lib/contentful.server.ts b/examples/remix/lib/contentful.server.ts
index 176f241d..8eed37e2 100644
--- a/examples/remix/lib/contentful.server.ts
+++ b/examples/remix/lib/contentful.server.ts
@@ -1,7 +1,43 @@
import { GraphQLClient } from 'graphql-request';
+import type { QueryResponse, Variables } from '../types'
-const SPACE = process.env.CONTENTFUL_SPACE_ID;
+const fetchData = async (spaceId: string, accessToken: string, query: string, variables: Variables, preview: boolean) => {
+ const client = new GraphQLClient(`https://graphql.contentful.com/content/v1/spaces/${spaceId}`);
+ const data = (await client.request(
+ query,
+ variables,
+ { authorization: `Bearer ${accessToken}` }
+ )) as QueryResponse;
-const endpoint = `https://graphql.contentful.com/content/v1/spaces/${SPACE}`;
+ return data;
+}
-export const contentful = new GraphQLClient(endpoint);
+// The `spaceId` and `accessToken` are passed as arguments from a `loader` function
+// to these utility functions. This approach avoids browser errors that occur when
+// trying to access `process.env` from this file, even though it is not executed in
+// the browser. This is likely a Remix bundling issue.
+export const getEntryBySlug = async ({
+ spaceId,
+ accessToken,
+ query,
+ slug,
+ preview,
+}: {
+ spaceId: string,
+ accessToken: string,
+ query: string,
+ slug: string,
+ preview: boolean,
+}) => fetchData(spaceId, accessToken, query, { slug, preview }, preview);
+
+export const getEntries = async ({
+ spaceId,
+ accessToken,
+ query,
+ preview,
+}: {
+ spaceId: string,
+ accessToken: string,
+ query: string,
+ preview: boolean,
+}) => fetchData(spaceId, accessToken, query, { preview }, preview);
diff --git a/examples/remix/types.d.ts b/examples/remix/types.d.ts
new file mode 100644
index 00000000..573bcd96
--- /dev/null
+++ b/examples/remix/types.d.ts
@@ -0,0 +1,24 @@
+export type Post = {
+ title: string;
+ description: string;
+ slug: string;
+ sys: {
+ id: string;
+ };
+};
+
+export type LoaderData = {
+ post: Post;
+ posts: Post[];
+ preview: boolean;
+};
+
+export type QueryResponse = {
+ postCollection: {
+ items: Post[];
+ };
+};
+
+export type Variables = {
+ [key: string]: string | boolean;
+};