Convert Everything to ComponentsBeta

This article will reinforce the concepts of breaking a website in to 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 in to Pieces.

(Remaining Components)

So now you know how to create a website block as a component with Nuxt.js and Prismic we'll look at doing the same with the remaining blocks from the homepage. So first let's look at 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 this process for each Slice.

2. The Full Width Image Slice

(Field Settings & Global Styles)

So we once more run the prismic sm --create-slice command and give the Slice the name FullWidthImage. We then 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 to a max width of 980px and max height of 300px.

Creating the Component:

After that we copy the suggested code and paste it in to the index.vue file for the FullWidthImage component. Below is the full component code.

Copy
<template>
  <section class='content-section container'>
    <prismic-image :field="slice.primary.image"/>
  </section>
</template>

<script>
export default {
  props: {
    slice: {
      type: Object,
      required: true,
      default() {
        return {}
      },
    },
  },
}
</script>

<style scoped>
@media (max-width: 767px) {
  .content-section {
    margin-bottom: 2rem;
  }
}
</style>

Global CSS

You can see from the code above we've added the class container. We called it here but we defined this CSS class globally so that we can use it in all our components that need it. We specified where this global CSS file lives in the nuxt.config.js file and give the container the following style. You'll also find the full global styles to include, like titles and links, in the 3rd tab below:

  • CSS
  • nuxt.config.js
  • Full Global CSS
Copy
.container {
  max-width: 980px;
  margin: auto;
}
Copy
  css: [
    "vue-essential-slices/src/styles/styles.scss",
    '@/assets/css/global.css',
  ],
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;
}
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 */
.container,
header,
footer {
  max-width: 980px;
  margin: auto;
}
.content-section {
  margin-bottom: 3.75rem;
}

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

Once this is done 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)

The following Slice is the first where we will utilise the repeatable zone in The Slice Builder. The repeatable zone is used for creating groups inside Slices. This means you can have multiple iterations of a group within the Slice in the document writing room, giving your content creators the power to add content for things like galleries, sliders, etc.

So for this Slice we'll need a 'Title' (galleryTitle) in the 'non-repeatable' zone and for the 'repeatable' zone we'll need:

  • Image - With a max width of 727px & a max height of 402px (image)
  • Image Description (imageDescription)
  • Link (link) + Link Label (linkLabel)

You Can See How We Add These Fields and Edit Their Settings in the Video Below:

Full 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 v-for loop. This is for the fields to be repeated whatever number of times the content writers use the group. For our example we need to combine all the fields from the repeatable zone in to one for loop as shown in the full component code & scoped style below.

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

Copy
<template>
  <section class='image-gallery content-section container'>
    <prismic-rich-text :field="slice.primary.galleryTitle"/>
    <div class="gallery">
      <div v-for="(item, i) in slice.items" :key="`slice-item-${i}`" class="gallery-item">
        <prismic-image :field="item.image"/>
        <prismic-rich-text :field="item.imageDescription"/>
        <p>
          <prismic-link :field="item.link" class="gallery-link">{{ item.linkLabel }}</prismic-link>
        </p>
      </div>
    </div>
  </section>
</template>
<script>
export default {
  props: {
    slice: {
      type: Object,
      required: true,
      default() {
        return {}
      }
    }
  },
}
</script>
<style scoped>
.gallery {
  display: -webkit-box;
  display: -ms-flexbox;
  display: -webkit-flex;
  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;
}

@media (max-width: 767px) {
  .content-section {
    margin-bottom: 2rem;
  }

  .gallery-item {
    -webkit-box-flex: 100%;
    -moz-box-flex: 100%;
    -webkit-flex: 100%;
    -ms-flex: 100%;
    flex: 100%;
  }
}
</style>

4. Featured Image

For the following Slice once again run the prismic sm --create-slice command and add the following fields in The Slice Builder:

Title

A Rich text field with only the H2 option selected. (title)

Headline

A Rich text field with only the H3 option selected. (headline)

Link

Link field

Label

Key text field (linkLabel)

Image

Image field with a max width of 727px & a max height of 402px (featuredImage)

Full component code:

Here's the full code, now simply add this & save, refresh the preview image and push your Slice to Prismic.

Copy
<template>
  <section class='highlight content-section container'>
    <div class="highlight-left">
      <prismic-rich-text :field="slice.primary.title"/>
      <prismic-rich-text :field="slice.primary.headline"/>
      <p>
        <prismic-link :field="slice.primary.link">{{ slice.primary.linkLabel }}</prismic-link>
      </p>
    </div>
    <div class="highlight-right">
      <prismic-image :field="slice.primary.featuredImage"/>
    </div>
  </section>
</template>

<script>
export default {
  props: {
    slice: {
      type: Object,
      required: true,
      default() {
        return {}
      },
    },
  },
}
</script>

<style scoped>
.highlight {
  position: relative;
  overflow: auto;
}

.highlight-left {
  width: 43%;
  float: left;
}

.highlight-right {
  width: 48%;
  float: right;
}

@media (max-width: 767px) {
  .content-section {
    margin-bottom: 2rem;
  }

  .highlight-left, .highlight-right {
    width: 100%;
    float: none;
  }
}
</style>

5. Quote Slice

This Slice is quite simple. Run the prismic sm --create-slice command and in The Slice Builder add a Rich text field with the API ID (quotetext) and only the p option for paragraphs selected.

This time for the component we won't use the suggested code from The Slice Builder, we'll use the asText method to print our quote. We use this method to strip away any extra formatting and take more control of the output so all our quotes look consistent. You can read more about the asText method here.

See the full component code and style below.

Copy
<template>
  <section class='content-section quote container'>
    <blockquote class="block-quotation">{{ $prismic.asText(slice.primary.quotetext) }}</blockquote>
  </section>
</template>

<script>
export default {
  props: {
    slice: {
      type: Object,
      required: true,
      default() {
        return {}
      },
    },
  },
}
</script>

<style scoped>
.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;
  quotes: "“" "”" "‘" "’";
  text-align: center;
}
.quote blockquote:before {
  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 {
  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;
  content: close-quote;
}

@media (max-width: 767px) {
  .content-section {
    margin-bottom: 2rem;
  }

  .quote {
    font-size: 20px;
  }
}
</style>

6. Text Slice

Finally the simplest Slice to create is the Text slice. Again run the prismic sm --create-slice command and in The Slice Builder add a Rich text field with the API ID (text). This is just a plain text block with all options for the content writers to use as they see fit. So there's no extra style etc. to add in the component, as seen below.

Copy
<template>
  <section class="container">
    <prismic-rich-text class="content-section" :field="slice.primary.text"/>
  </section>
</template>

<script>
export default {
  props: {
    slice: {
      type: Object,
      required: true,
      default() {
        return {}
      },
    },
  },
}
</script>

<style scoped>
</style>

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