CoursesEditorialized ecommerce experiencesCreating campaign pages

Editorialized ecommerce experiences

Lesson
1

Creating campaign pages

With Sanity, Shopify and Hydrogen connected it's time to build custom, highly curated landing pages with fine-grained control over presentation.

Simeon Griggs
Simeon GriggsPrincipal Educator at Sanity
Before starting this Course it may be beneficial to have completed:
Log in to mark your progress for each Lesson and Task

In Sanity and Shopify with Hydrogen you setup the basics to connect Sanity, Shopify and a Hydrogen front end. With these systems now all working together it's time to create rich experiences.

In the following exercises you'll create a campaign page for a specific set of products. This is the sort of editorial work your content creators are likely going to need to do on a regular basis.

With Sanity, you can prepare the building blocks they need to instantly create new landing pages while benefiting from referencing up to date external content like product data.

The shopify Studio template you've used comes with a number of pre-configured blocks which you could add to or modify. They've been made available to this portable text field.

In your Studio, create a new page type document.

In your Sanity Studio, take a look at the portableTextType.ts schema type file. It is an array type field that first contains a block type – and then multiple other object types. That's where this list was configured.

The Portable Text editor is not just for rich text, but also block content!

Looking at page documents in the Studio, the body field is just an array type field. But, since one of its fields is a block type field, Sanity Studio presents renders it with the Portable Text editor – instead of the default array type UI.

The Portable Text editor allows you to write rich text and block content.

While the data is the same – an array of objects – no matter which editor you use, for this module we're making the editorial decision that page content will only ever be block content. For this the default array editor is simpler to use.

Let's copy the block types from this portable text field and replace the body field in page documents.

Update the body field in your page type schema
./schemaTypes/documents/pageType.ts
defineField({
name: 'body',
type: 'array',
group: 'editorial',
of: [
defineArrayMember({ type: 'accordion' }),
defineArrayMember({ type: 'callout' }),
defineArrayMember({ type: 'grid' }),
defineArrayMember({ type: 'images' }),
defineArrayMember({ type: 'imageWithProductHotspots', title: 'Image with Hotspots' }),
defineArrayMember({ type: 'instagram' }),
defineArrayMember({ type: 'products' }),
]
}),

The body field should now look like this:

You can start creating content with these blocks, but most importantly, you'll need to update your Hydrogen front end to render them.

For a smoother developer experience in the following exercises, generate Types from your Studio schema and copy the definitions into the Hydrogen app.

Read more about Sanity TypeGen in the documentation

Inside your Studio project:

Extract the current schema types to a JSON file
Terminal
npx sanity@latest schema extract
Create a typegen configuration file so that a file containing your Types are written directly to your Hydrogen app when typegen generate is run in your Studio
./sanity-typegen.json
{
"path": "../sanity-and-hydrogen/app/sanity/queries.ts",
"schema": "schema.json",
"generates": "../sanity-and-hydrogen/app/sanity/sanity.types.ts"
}
Generate types from that JSON file
Terminal
npx sanity@latest typegen generate

You should now have a file sanity.types.ts inside your Hydrogen app.

Now in your Hydrogen app, you'll need to setup a route to render page type documents.

Create a new document to contain your GROQ queries
./app/sanity/queries.ts
import groq from 'groq';
export const PAGE_QUERY = groq`*[_type == "page" && slug.current == $slug][0]`
Create a new route in your Hydrogen app
./app/routes/$slug.tsx
import {json, type LoaderFunctionArgs} from '@shopify/remix-oxygen';
import {useLoaderData} from '@remix-run/react';
import type {PAGE_QUERYResult} from '~/sanity/sanity.types';
import {PAGE_QUERY} from '~/sanity/queries';
const BLOCKS: Record<string, (props: any) => JSX.Element | null> = {
_unknown: (props: any) => <pre>{JSON.stringify(props, null, 2)}</pre>,
};
export async function loader({params, context: {sanity}}: LoaderFunctionArgs) {
const query = PAGE_QUERY;
const initial = await sanity.loadQuery<PAGE_QUERYResult>(query, params);
if (!initial.data) {
throw new Response('Not found', {status: 404});
}
return json({initial});
}
export default function Page() {
const {initial} = useLoaderData<typeof loader>();
const page = initial?.data;
return Array.isArray(page?.body) ? (
<main>
{page.body.map((block) =>
block._type in BLOCKS
? BLOCKS[block._type]({key: block._key, ...block})
: BLOCKS._unknown({key: block._key, ...block}),
)}
</main>
) : null;
}

This route renders each array field item as a unique component. If a matching component is not found, the item's content is stringified and rendered to the page.

Back in your Sanity Studio, create a page

Publish a new page document with the title "The Blue Collection" and generate a slug
Add an "Accordion" block to the body field

Visit http://localhost:3000/the-blue-collection and you should see a page something like this:

Now, let's style this "accordion" block.