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.
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 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.
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.
body
field in your page
type schemadefineField({ 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.
Inside your Studio project:
npx sanity@latest schema extract
typegen generate
is run in your Studio{ "path": "../sanity-and-hydrogen/app/sanity/queries.ts", "schema": "schema.json", "generates": "../sanity-and-hydrogen/app/sanity/sanity.types.ts"}
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.
import groq from 'groq';
export const PAGE_QUERY = groq`*[_type == "page" && slug.current == $slug][0]`
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
slug
body
fieldVisit http://localhost:3000/the-blue-collection and you should see a page something like this:
Now, let's style this "accordion" block.