Create More Components

Beta

These docs rely on Slice Machine, which is in active development and subject to change. Content Relationships are not currently supported with Slices.

This article will reinforce the concepts of breaking a website into blocks, creating Slices, and developing components with Prismic. You'll also learn about the field settings, the repeatable zone, and global styles.


1. Break the design into pieces.

Now that you have created your first component for the banner, we'll look at doing the same with the remaining blocks from the homepage. So first let's look at the video below to see how we should divide up these blocks.

From the video above you can see that we have 5 blocks that we will recreate as Slices and components.

  • Full width image
  • Image Gallery
  • Featured Image
  • Quote
  • Text

Below we'll take you through the process of creating each Slice.

2. The 'Full Width Image' Slice

So again we click the + button to create a new Slice and then give the Slice the name FullWidthImage.

Field settings

In the Slice Builder, delete the existing fields and add an image field with the name (API ID) of image. Then in the field settings ⚙️, we set the restraints on the image: max-width of 980px and max height of 300px. Save the model to the filesystem.

Add fields example video

Component code

We're going to use the suggested code and paste it into the index.js file. Below is the full component code:

Copy
import React from 'react';

const FullWidthImage = ({ slice }) => (
  <section className="container">
    <img src={slice.primary.image.url} alt={slice.primary.image.alt} />
  </section>
);

export default FullWidthImage;

Global CSS styles

You can see from the code above that we've added the class called container to the section HTML tag. We added it here but we defined the style for this class globally so that we can use it in any components that need it, you can find the globals.css file in the project directory at ../styles/globals.css. We import this file in the ../pages/_app.js file so that it's used everywhere.

You can see the full CSS of this file and the class we added below (In the 2nd tab, you'll see the full global CSS):

  • CSS Class
  • ../styles/globals.css
Copy
.container {
  max-width: 980px;
  margin: auto;
  margin-bottom: 3.75rem;
}
Copy
* {
  -webkit-font-smoothing: antialiased;
}

*,
*::before,
*::after {
  box-sizing: content-box;
}

::selection {
  background: #fff7c7; /* WebKit/Blink Browsers */
}
::-moz-selection {
  background: #fff7c7; /* Gecko Browsers */
}

/*
 * Globals
 */
body {
  color: #72767b;
  font-family: "Lato", sans-serif;
  font-size: 16px;
  font-weight: 400;
  letter-spacing: 0.4px;
  line-height: 28px;
  margin: 0;
}
a {
  color: #72767b;
  font-size: 14px;
  font-weight: 400;
  letter-spacing: 0.35px;
  line-height: 28px;
  text-decoration: none;
}
p a {
  text-decoration: underline;
}
h2,
h3,
h4,
h5,
h6 {
  font-family: "Lato", sans-serif;
}
h1 {
  font-size: 42px;
  font-weight: normal;
  color: #484d52;
  line-height: 52px;
  letter-spacing: 1.14px;
  margin-bottom: 1rem;
}
h2,
h2 a {
  margin-bottom: 1rem;
  color: #484d52;
  font-size: 32px;
  font-weight: 700;
  letter-spacing: 0.85px;
  line-height: 42px;
}
h3,
h3 a {
  margin-bottom: 1rem;
  color: #484d52;
  font-size: 20px;
  font-weight: 400;
  letter-spacing: 0.52px;
  line-height: 34px;
}
p,
pre,
ul,
ol {
  margin-bottom: 2rem;
}
ul {
  padding-left: 35px;
  list-style: initial;
}
ol {
  padding-left: 35px;
  list-style: decimal;
}
strong {
  font-weight: bold;
}
em {
  font-style: italic;
}
img {
  max-width: 100%;
}

/* General */
header,
footer {
  max-width: 980px;
  margin: auto;
}
.container {
  max-width: 980px;
  margin: auto;
  margin-bottom: 3.75rem;
  clear: both;
}

@media (max-width: 767px) {
  h1 {
    font-size: 32px;
    line-height: 40px;
  }
  h2 {
    font-size: 26px;
  }
  h3 {
    font-size: 18px;
  }
  ul {
    padding-left: 20px;
  }
}

Using global CSS with Storybook

To use global CSS files like this with the Storybook (The local component development environment) you need to declare it in the Storybook settings file located ../.storybook/main.js. Here you'll need to add the path to your CSS file, you can see what this file should look like with the path to your CSS below.

Copy
const { getStoriesPaths } = require('slice-machine-ui/helpers/storybook')
module.exports = {
  stories: [...getStoriesPaths(), "../styles/globals.css"]
}

Once you do this you can check out your full width image component in Storybook and Push the Slice in the the Slice Builder.


3. The 'Image Gallery' Slice

The Repeatable Zone

In this Slice we will use the repeatable zone in the Slice Builder. The repeatable zone allows you to have multiple iterations of a group of fields within a Slice. This gives your content creators the power to add content for things like galleries, sliders, etc.

Below you can see the fields we'll need and in which zones.

Non-repeatable zone

galleryTitle

Rich Text field, with only the H2 option selected

Repeatable zone

image

Image field, with a max width of 727px & a max height of 402px

imageDescription

Rich Text field

link

Link field

linkLabel

Key Text field

Add fields example video

Component code

You'll see when creating your Slice that the suggested code snippets for the fields in the repeatable zone come wrapped in a map array method, so the fields will repeat for each item in the group.

In this example, we're going to use one loop for all fields from the repeatable zone as shown in the full component code and JSX Style below.

Once this is added, refresh the preview image and push your Slice to Prismic.

Copy
import React from 'react'
import { RichText } from 'prismic-reactjs'
import { Link } from 'prismic-reactjs'

const ImageGallery = ({ slice }) => (
  <section className="image-gallery container">
    <RichText render={slice.primary.galleryTitle} />
    <div className="gallery">
      { slice?.items?.map((item, i) =>
        <div key={i} className="gallery-item">
          <img
            src={item.image.url}
            alt={item.image.alt}
          />
          <RichText render={item.imageDescription}/>
          <p>
            <a className="gallery-link" href={Link.url(item.link)}>
              <span>{item.linkLabel}</span>
            </a>
          </p>
        </div>
      )}
    </div>
    <style jsx>{
      `
      .image-gallery {
        margin-bottom: 3.75rem;
        padding: 20px;
        color: #72767b;
        font-family: 'Lato', sans-serif;
        font-size: 16px;
        font-weight: 400;
        letter-spacing: 0.4;
        line-height: 28px;
      }
      .gallery {
        display: -webkit-box;  /* OLD - iOS 6-, Safari 3.1-6, BB7 */
        display: -ms-flexbox;  /* TWEENER - IE 10 */
        display: -webkit-flex; /* NEW - Safari 6.1+. iOS 7.1+, BB10 */
        display: flex;
        -webkit-flex-wrap: wrap;
        flex-wrap: wrap;
        -webkit-justify-content: space-between; 
        justify-content: space-between; 
      }
      .gallery-item {
        -webkit-box-flex: 0 1 48%;
        -moz-box-flex:  0 1 48%;
        -webkit-flex:  0 1 48%;
        -ms-flex:  0 1 48%;
        flex: 0 1 48%;
      }
      .gallery-link {
        margin-top: -20px;
        text-transform: uppercase;
      }
      .gallery img {
        margin-bottom: 1rem;
      }
      @media (max-width: 767px) {
        .image-gallery {
          margin-bottom: 2rem;
        }
        .gallery-item {
          -webkit-box-flex: 100%;
          -moz-box-flex:  100%;
          -webkit-flex:  100%;
          -ms-flex:  100%;
          flex: 100%;
        }
      }
      `
    }</style>
  </section>
)

export default ImageGallery

4. Featured Image

Again, press the + button to create a FeaturedImage slice, and add the following fields in the Slice Builder.

Fields to add

title

Rich text field, with only the H2 option selected.

headline

Rich text field, with only the H3 option selected.

link

Link field

linkLabel

Key text field

featuredImage

Image field, with a max width of 727px & a max height of 402px

Component code

Here's the full component code & styled-components. Copy this code into the file ~/slices/FeaturedImage/index.js and save it. Then, go back to the Prismic Builder, refresh the preview image and push your Slice to Prismic. In this example, we've used CSS grid to create our columns.

Copy
import React from 'react'
import { RichText } from 'prismic-reactjs'
import { Link } from 'prismic-reactjs'

const FeaturedImage = ({ slice }) => (
  <section className="highlight container">
    <div>
      <RichText render={slice.primary.title} />
      <RichText render={slice.primary.headline} />
      <p>
        <a href={Link.url(slice.primary.link)}>
          <span>{slice.primary.linkLabel}</span>
        </a>
      </p>
    </div>
    <div>
      <img
        src={slice.primary.featuredImage.url}
        alt={slice.primary.featuredImage.alt}
      />
    </div>
    <style jsx>{`
      .highlight {
        display: grid;
        grid-template-columns: 1fr 1fr;
        column-gap: 25px;
      }
      @media (max-width: 767px) {
        .highlight {
          grid-template-columns: 1fr;
        }
      }
      `}</style>
  </section>
)

export default FeaturedImage

5. Text Slice

Now, we're going to create a text block that can be either 1 or 2 columns called TextSlice. First, in the Slice Builder, add the following field.

text

Rich Text field

To give our content creators the ability to choose between options (1 or 2 columns), we use the 'Variations' feature. To create a variation select the dropdown on the top of the Builder and click the '+ Add new variation' option.

The '+ Add new variation' button.
The '+ Add new variation' button.

The 'Add new variation' screen has 3 options:

  • Variation Name: This is the label that you will see in the Builder and your Prismic documents.
  • Variation ID: This is the API ID in the Slice Model which can be used in your component to create variations.
  • Duplicate from: Here you select which of the existing variations you wish to base your new variation on.

We will call our Variation 'Two Column; which creates the ID twoColumn and we'll duplicate it from the default Slice.

The 'Add new variation' screen.
The 'Add new variation' screen.

Component code

In the component code, use the field slice.variation from the Slice model to create the difference in the component. For this example, we use it as a class name by which to change the column count of the text block. Save, then check Storybook to see the Variations with mock content.

Copy
import React from 'react';
import { RichText } from 'prismic-reactjs';

const TextSlice = ({ slice }) => (
  <section className={`container ${ slice.variation }`}>
    <RichText render={slice.primary.text} />
    <style jsx>{`
      .default-slice {
        column-count: 1;
      }
      .twoColumn {
        column-count: 2;
      }
    `}</style>
  </section>
);

export default TextSlice;

Preview your 'Variations'

You can then head over to Storybook to check out how your variations will differ.

Pro Tip: Edit your Mock Config for the text field in the Prismic Builder to add more text block to really show the difference between your variations.


6. Quote Slice

For this 'Quote' Slice we're are also going to add a variation, we're going to give the content creators the option to add a reference to the quote that they use. So to make the Slice click the 'Create Slice +' button, create a QuoteSlice and add a quotetext rich text field in the Builder.

Then to create a variation select the dropdown on the top of the Builder and click the '+ Add new variation' option, call it 'Quote Reference' which will give you an API ID of quoteReference for the variation. Then in this variation add another Rich Text field called reference. All of these fields are described below:

Fields to add

quotetext

Rich text field with only the p option for paragraphs selected.

reference (Variation)

Rich text field with only the p, bold, italic and link options for paragraphs selected.

Add variation example video:

This time instead of using the suggested code for the quotetext field we'll use the asText method to print our quote to strip away any extra formatting and take more control of the output so all our quotes look consistent. Then for the reference field we'll conditionally render it if the content creator chooses the quoteReference variation.

Component code

Here's the full component code & style:

Copy
import React from 'react'
import { RichText } from 'prismic-reactjs'

const QuoteSlice = ({ slice }) => (
  <section className="container quote">
    <blockquote>
      {RichText.asText(slice.primary.quotetext)}
    </blockquote>
    { slice.variation === 'quoteReference' ? 
      <div>
        <cite>
          <RichText render={slice.primary.reference}/>
        </cite>
      </div>
      : null
    }
    <style jsx>{`
      .quote blockquote {
        display: block;
        font-family: 'Lora', Serif; 
        font-size: 36px;
        font-style: italic;
        font-weight: normal; 
        color: #484D52; 
        letter-spacing : 1.14;
        line-height: 1.5em;
        text-align: center;
      }
      .quote blockquote:before,
      .quote blockquote:after {
        color: #e9e9e9;
        content: open-quote;
        font-family: 'Lora', Serif;
        font-size: 2.5em;
        font-weight: 900;
        line-height: 0.1em;
        margin-left: 10px;
        margin-right: 10px;
        vertical-align: -0.3em;
      }
      .quote blockquote:after {
        content: close-quote;
      }
      .quote div {
        width: 300px;
        float: right;
        margin: -36px 0px 0px 0px;
      }
      .quote div cite {
        display: flex;
        line-height: 18px;
      }
      .quote div cite:before {
        content: '-';
        margin: 16px 4px 0px 0px;
      }
      @media (max-width: 767px) {
        .quote {
          font-size: 20px;
        }
      }
    `}</style>
  </section>
)

export default QuoteSlice

Congratulations! You've now created all the components and Slices that you'll need for you website. Next we're going to learn how to see these components with live data from our CMS.


Next and previous articles


Was this article helpful?
Not really
Yes, Thanks

Can't find what you're looking for? Get in touch with us on our Community Forum.