Template Content

This technology has no Slice Machine integration

This framework has no integration with Prismic's developer tool, Slice Machine. You can still build a Prismic website with this technology by using Prismic's Legacy Builder. However, if you're starting a new project with Prismic, we strongly recommend using a technology that integrates with Slice Machine: Next.js or Nuxt.

On this page, you'll learn how to template content from the Prismic API in your Express application.


Intro to templating

Content from Prismic comes in different types. Some are simple fields, like numbers or booleans. Others are structured fields, like titles, rich texts, and links.

Before Reading

This article assumes that you have queried your API and saved the document object in a variable named document, as described in Fetch Data.

Simple field types can be injected directly into your application:

Copy
<span>$<%- document.data.example_number %></span>
<!-- <span>$74.5</span> -->

@prismicio/client includes special utilities for rendering structured fields. To render the rich text field, for instance, use prismic.asHTML():

Copy
<%- ctx.prismic.asHTML(document.data.example_rich_text) %>

To get started, let's look at the structure of the API response.

The structure of the API response

When you use Prismic's query methods, you will see three different types of API responses:

  • paginated responses, which return a response object
  • get-all responses, which return a results array
  • get-single responses, which return a document object

In fact, these are all part of the same structure: the response object contains the results array, in which each item is a document object.

Here's more detail about what that each looks like:

The response object

When you make a query by get() to the Prismic API, you will get a JSON object containing pagination information and an array of query results. Here is a truncated example of an API response containing some simple fields:

Copy
{
  page: 1,
  // ...
  results: [
    {
      uid: 'example_document',
      // ...
      data: {
        example_date: '2020-12-10',
        example_timestamp: '2020-12-10T04:05:09+0000',
        example_color: '#c7ab5d',
        example_number: 74.5,
        example_key_text: 'Example Key Text Value',
        example_select: 'North',
        example_boolean: true,
      }
    },
    // ...
  ]
}

If you store the response in a variable called response you might access a single data point like this:

Copy
response.results[0].data.example_key_text 
// 'Example Key Text Value'

In Express with, EJS templating, like this:

Copy
<h3><%- response.results[0].data.example_key_text %></h3>
<!-- <h3>Example Key Text Value</h3> -->

The most important property on the response object is the results array.

The results array

When you make a query using a get-all method, such as getAllByType(), you will get a results array containing all matching results.

The getAll*() methods, such as getAllByType(), will directly return a results array.

Here is a truncated example of an API response using getAllByType() :

Copy
[
  {
    uid: 'example_blog_post',
    type: 'blog_post',
    data: {
      example_date: '2020-12-10',
      example_key_text: 'Example Key Text Value',
    }
  },
  {...},
  {...},
  {...}
]

If you store the response in a variable called results you might access a single data point like this:

Copy
results[0].data.example_key_text
// Example Key Text Value

In Express.js with, EJS templating, like this:

Copy
<h3><%- results[0].data.example_key_text %></h3> 
<!-- <h3>Example Key Text Value</h3> -->

Each item in the results array is a document object.

The document object

The helper functions getSingle()getByUID(), and getByID() will return a document object, which contains the data for an individual document directly.

Here's a truncated example of the response for those queries:

Copy
{
  uid: 'about',
  // Metadata for this result
  // ...
  data: {
    example_date: '2020-12-10',
    example_color: '#c7ab5d',
    example_key_text: 'Example Key Text Value',
  }
}

With a single document query, you might access your data like this:

Copy
<h3><%- document.data.example_key_text %></h3> 
<!-- <h3>Example Key Text Value</h3> -->

Simple fields

The content for each Prismic field is delivered as either a simple primitive value or an object. The simple fields can be injected directly into your app since their value is either a string, a number, or a boolean. Here are the simple fields:

  • Color
  • Key text
  • Number
  • Select
  • Boolean
  • Date
  • Timestamp

Here's an example of how to template the number field value from its API ID. You can template the other simple fields the same way.

Copy
<h3>$<%- document.data.example_number %></h3>
<!-- <h3>$74.5</h3> -->

Color

Retrieving the color field is similar. Here we can see how to do inline styling in Express:

Copy
<h3 style="color: <%= document.data.example_color %>">Colorful Title</h3>
<!-- <h3 style="color: #FF0000">Colorful Title</h3> -->

Date and timestamp

The date and timestamp fields from Prismic are strings. The raw response for each of these fields has the following formats:

  • Date: YYYY-MM-DD
  • Timestamp: YYYY-MM-DDTHH:MM:SS+0000

prismic.asDate()  takes a date or timestamp field from Prismic and converts it to a JavaScript date object. To learn more about JavaScript Date objects, see the MDN Date documentation or this tutorial by Tania Rascia.

Copy
 <!-- Date field -->
<span>
  <%- ctx.prismic.asDate(document.data.example_date).getFullYear() %>
</span>
<!-- <span>2021</span> -->


<!-- Timestamp field -->
<span>
  <%- ctx.prismic.asDate(document.data.example_timestamp).toISOString() %>
</span>
<!-- <span>2018-01-16T13:46:15.000Z</span> -->

Geopoint

The geopoint field is served as an object with two properties: latitude and longitude. Here is an example API response:

Copy
example_geopoint: {
  latitude: 48.85392410000001,
  longitude: 2.2913515000000073
}

Here is an example of how to retrieve the latitude & longitude coordinates for a geopoint field.

Copy
<% const { latitude, longitude } = document.data.example_geopoint %>

<p>My location: <%= latitude %>, <%= longitude %></p>
<!-- <p>My location: 48.85392410000001, 2.2913515000000073</p> -->

Embed

The embed field will let content authors paste an oEmbed supported service resource URL (YouTube, Vimeo, Soundcloud, etc.), and add the embedded content to your website. This is the API response of the embed field:

Copy
example_embed: {
  version: '1.0',
  url: 'https://prismic.io',
  type: 'link',
  title: 'Make your website editable for the whole team - Prismic',
  provider_name: null,
  thumbnail_url:
    'https://images.prismic.io/prismic-     website/6e49007fec52f99861047cdd55b3ab190ea04295_dev-landing-page-image.png?auto=compress,format',
  html: '<div data-type="unknown"><a href="http...',
  embed_url: 'https://prismic.io/',
}

Here’s an example of how to integrate the embed field into your templates.

Copy
<%- document.data.example_embed.html %>

Images

The image field returns an object with data about the image, including a URL for your image (hosted on Prismic's servers) and alt text.

Copy
example_image : {
  dimensions: {
    width: 1920,
    height: 1302
  },
  alt: "cool cat pic",
  copyright: null,
  url: "https://images.prismic.io/sm-20201204-2/7d1fba99-5bec-4d59-b8eb-402706e2d36c_a-pril-62u95KgB49w-unsplash.jpg?auto=compress,format"
}

You can template an image using the asImageSrc() function and an <img> element:

Copy
<% const image = prismic.asImageSrc(document.data.example_image) %>

<img 
  src="<%- image.src %>"
  alt="<%- document.data.example_image.alt %>"
/>

Images can be transformed using Prismic’s built-in Imgix integration and asImageSrc(). This allows you to resize, crop, recolor, and more. See Imgix’s URL API Reference for a full list of available information.

The following example converts the image to grayscale with sat: -100:

Copy
<% const image = prismic.asImageSrc(document.data.example_image, { sat: -100 }) %>

<img 
  src="<%- image.src %>"
  alt="<%- document.data.example_image.alt %>"
/>

Image fields can have responsive views. Retrieve their URLs by passing each view to asImageWidthSrcSet().

The asImageWidthSrcSet() and asImagePixelDensitySrcSet() functions build responsive srcset attribute values that can be passed to <img> or <source> elements:

Copy
<% const image = prismic.asImageWidthSrcSet(document.data.example_image) %>

<img 
  src="<%- image.src %>"
  srcset="<%- image.srcset %>"
  alt="<%- document.data.example_image.alt %>"
/>

Rich text and titles

Rich text and titles are delivered in an array that contains information about the text structure. Here's an example of the API response of the rich text field (title fields follow the same format).

Copy
example_rich_text: [
  {
    type: "paragraph",
    text: "Example Rich Text Value",
    spans: [
      {
        start: 8,
        end: 17,
        type: "strong"
      }
    ]
  }
]

Learn more about rich text

To learn more about how rich text is structured in Prismic and how to configure it, see the rich text article.

Output as HTML

To render the rich text and title fields, use the prismic.asHTML() method to convert your rich text field to HTML.

Copy
<%- ctx.prismic.asHTML(document.data.example_rich_text) %>

Modify rich text HTML

You can modify the markup of a rich text field with a rich text serializer function. Learn more about the rich text serializer.

Output as plain text

The prismic.asText() method will output the rich text or title field as a string. Here is an example of how to get plain text from a rich text or title field:

Copy
<h3><%- ctx.prismic.asText(document.data.example_rich_text) %></h3>
<!-- <h3>Example rich text</h3> -->

Links and content relationship

The route resolver

Prismic does not know the routing of your Express app. You can tell Prismic the structure of your app with a route resolver when you create a client. (You should have already created a route resolver when you Installed Prismic in your app.) Prismic will then use that information to add a url property to each document.

To learn more about creating URLs, see our article on the route resolver.

The link field allows you to link to an external webpage, an internal Prismic document, or an item in your Media Library (like a PDF). The content relationship field allows you to link specifically to an internal Prismic document.

Here's what a content relationship field looks like from the API (a link field takes a similar format). This field has an API ID example_content_relationship, and it links to another document that has the UID another-document and the type page.

Copy
example_content_relationship: {
  id: "X9C65hEAAEFIAuLo",
  type: "page",
  tags: [],
  slug: "another-document",
  lang: "en-us",
  uid: "another-document",
  link_type: "Document",
  isBroken: false
}

There are two things that you might want to do with a link:

  • Link to another page or media item, internally or externally
  • Pull in content from another document

Here's how to do those things.

Link internally or externally

To render a link to the web, you can use the asLink() method from @prismicio/client. asLink() accepts a link or content relationship field and returns an absolute or relative URL as a string. You can inject that string into an <a> tag, like so:

Copy
<% const mediaUrl = ctx.prismic.asLink(document.data.example_link) %>
<a href="<%= mediaUrl %>">Click here</a>

Pull in data from another document

To pull content from a linked document, you must fetch that content in your API Query using the graphQuery or fetchLinks API options. You can learn more about both options in the Rest API Technical Reference. Here, we'll explain more about fetchLinks.

The fetchLinks option allows you to retrieve a specific content field from a linked document and add it to the document response object. In your query options, use fetchLinks as the key, with the value formatted like so:

  • first, the API ID of the custom type referenced in your content relationship field
  • then, the API ID of the field that you want to retrieve

For instance, if you have a custom type called blog that includes a content relationship field called example_content_relationship linked to a custom type called author where you want to retrieve a field that has an API ID is author_name, you would format the option like so:

Copy
const document = await client.getByUID('blog','my-blog-post', {
  'fetchLinks': 'author.author_name'
})

Once you have adjusted your API query, the linked content will appear in a data object nested in the link or content relationship field:

Copy
example_content_relationship: {
  id: "X9C65hEAAEFIAuLo",
  type: "blog",
  tags: [],
  slug: "another-document",
  lang: "en-us",
  uid: "my-blog",
  data: {
    author_name: "John Doe"
  },
  link_type: "Document",
  isBroken: false
}

You can then template that content normally:

Copy
<strong> 
  By <%- document.data.example_content_relationship.data.author_name %>
</strong>
<!-- <strong>By John Doe</strong> -->

Groups

A group field renders an array of content fields. Here's what the API response looks like for a hypothetical group field modeling a nav menu.

Copy
example_group: [
  {
    example_nav_label: "Homepage",
    example_nav_link: {...},
  },
  {
    example_nav_label: "Post",
    example_nav_link: {...},
  },
]

To integrate a repeatable group field into your template, loop through each item in the group as shown below using the map() method:

Copy
<ul>
  <% document.data.example_group.map(item => { %>
    <li>
      <a href="<%- ctx.prismic.asLink(item.example_nav_link) %>">
        <%- item.example_nav_label %> 
      </a>       
    </li>
  <% }) %>
</ul>

Slices

Slices are repeatable, rearrangeable content sections. Learn more about slices in What is a Slice?

On the document object, slices are stored in the body array, like so:

Slices vs groups

Slices and groups both allow you to create repeatable content templates. Groups are available in the static section of your document, and slices are available in the SliceZone. The difference between groups and slices is that groups can only be repeated, while slices can be repeated and mixed.

Copy
body: [
  {
    slice_type: 'text_slice',
    slice_label: null,
    items: [{}],
    primary: {
      key_text: 'Some example text in a slice.',
    },
  },
  {
    slice_type: 'image_gallery_slice',
    slice_label: null,
    items: [
      {
        gallery_image: {
          dimensions: { … },
          alt: 'An illustration of a cool dancing boy.',
          copyright: null,
          url: 'https://images.prismic.io/slicemachine-blank/6b2bf485-aa12-44ef-8f06-      dce6b91b9309_dancing.png?auto=compress,format',
        },
      },
    ],
    primary: {
      gallery_title: 'The title of this gallery',
    },
  },
]

To learn how to render slices, see the Render Slices article.


Was this article helpful?
Not really
Yes, Thanks

Can't find what you're looking for? Spot an error in the documentation? Get in touch with us on our Community Forum or using the feedback form above.