# Creating campaign pages https://www.sanity.io/learn/course/editorialized-ecommerce-experiences/creating-campaign-pages.md 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](https://www.sanity.io/learn/course/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. 1. In your Studio, **create** a new page type document. ![Portable text field showing available blocks](https://cdn.sanity.io/images/3do82whm/next/6ed6d69b54d7977f22d47444c20e9287f2a6a8e6-2144x1388.png) 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. 1. The Portable Text editor is not just for rich text, but also block content! ## Page building 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. 1. **Update** the `body` field in your `page` type schema ```typescript:./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: ![Sanity Studio with array field open](https://cdn.sanity.io/images/3do82whm/next/6eb2ce535406b7ba38ccfefb36953a7c171cf8e0-2144x1388.png) You can start creating content with these blocks, but most importantly, you'll need to update your Hydrogen front end to render them. ### Generate Types For a smoother developer experience in the following exercises, generate Types from your Studio schema and copy the definitions into the Hydrogen app. 1. Read more about [Sanity TypeGen](https://www.sanity.io/learn/apis-and-sdks/sanity-typegen) in the documentation Inside your Studio project: 1. **Extract** the current schema types to a JSON file ```text:Terminal npx sanity@latest schema extract ``` 1. **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 ```json:./sanity-typegen.json { "path": "../sanity-and-hydrogen/app/sanity/queries.ts", "schema": "schema.json", "generates": "../sanity-and-hydrogen/app/sanity/sanity.types.ts" } ``` 1. **Generate** types from that JSON file ```text:Terminal npx sanity@latest typegen generate ``` You should now have a file `sanity.types.ts` inside your Hydrogen app. ## Create a page route Now in your Hydrogen app, you'll need to setup a route to render `page` type documents. 1. **Create** a new document to contain your GROQ queries ```typescript:./app/sanity/queries.ts import groq from 'groq'; export const PAGE_QUERY = groq`*[_type == "page" && slug.current == $slug][0]` ``` 1. **Create** a new route in your Hydrogen app ```tsx:./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 JSX.Element | null> = { _unknown: (props: any) =>
{JSON.stringify(props, null, 2)}
, }; export async function loader({params, context: {sanity}}: LoaderFunctionArgs) { const query = PAGE_QUERY; const initial = await sanity.loadQuery(query, params); if (!initial.data) { throw new Response('Not found', {status: 404}); } return json({initial}); } export default function Page() { const {initial} = useLoaderData(); const page = initial?.data; return Array.isArray(page?.body) ? (
{page.body.map((block) => block._type in BLOCKS ? BLOCKS[block._type]({key: block._key, ...block}) : BLOCKS._unknown({key: block._key, ...block}), )}
) : 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. ### Create a page Back in your Sanity Studio, create a page 1. **Publish** a new page document with the title "The Blue Collection" and generate a `slug` 2. **Add** an "Accordion" block to the `body` field Visit [http://localhost:3000/the-blue-collection](http://localhost:3000/the-blue-collection) and you should see a page something like this: ![A plain webpage showing JSON data](https://cdn.sanity.io/images/3do82whm/next/63f8fd1986db9ad161e802dce756204bc8d127a2-2144x1388.png) Now, let's style this "accordion" block.