# VuePress Tutorial 17 - IndexPost Layout

By: Jay the Code Monkey
Posted: Oct 17, 2022 Updated: Apr 18, 2023

# What We're Doing

We're now ready to begin the development of the IndexPost layout component. We'll be using the globally scoped $pagination variable provided by the blog plugin Client API (opens new window) to access the pagination data. In this tutorial we'll be focusing on the post title and preview pagination data. To display the data on the pagination pages we'll be updating the IndexPost layout component's template tag.

Make sure you start the local development server which should be running at http://localhost:8080/ (opens new window) to see the changes we'll be making to the site. If the changes aren't appearing after you save them, then try restarting your local development server.

Be sure to add each block of code below one at a time to your project, then view the changes in the browser to get a better understanding of what each block is responsible for.

You can view all of the code in this tutorial by going to the tutorial-17 branch of the code-monkeys-blog-tutorials (opens new window) repository.

# Naming the Component

Before accessing and displaying the pagination data, we're going to first give the component a name and remove the created lifecycle hook which we were using in the previous tutorial to log the pagination data to the console.

Here's the updated IndexPost.vue file:

# Looping Over Pagination Pages

To display the pagination data we're going to loop over the $pagination.pages property which if you remember from the previous tutorial VuePress Tutorial 16 - Pagination is an array of objects where each object contains data related to post pages that are accessible on the current pagination page.

# Using the v-for Directive

Since $pagination.pages is an array that we want to loop over, we're going to use the v-for directive to render a list of post pages based on the data within the array. The v-for directive uses the following syntax v-for="item in items" where items is the array you want to loop over, and item is an alias for the array element being iterated on. Here, items corresponds to $pagination.pages, and we'll use post as the alias for the array element being iterated on. This means our v-for directive will be v-for="post in $pagination.pages".

# Using the key Attribute

When using the v-for directive the common best practice is to bind a key attribute where each value given to the key attribute should be unique. The key attribute uses the following syntax :key="item.id" where id is a property with a unique value for every item in the items array.

You really only need to use the key attribute when the rendered list relies on child component state or temporary Document Object Model (DOM) (opens new window) state, e.g., form input values . This means we don't actually need to include the key attribute since we're currently just rendering a static list.

However, we're going to bind the key attribute in case we ever need to use it in the future. To ensure the key attribute has a unique value for each item in the list, we're going to use the key property which is a unique value generated for each page object within the $pagination.pages array. Here's what the key attribute will look like in our case :key="post.key".

# Determining a Tag

Now we need to determine what tag we want to add our v-for directive and key attribute to. We're going to add them to a div tag which will allow us to wrap the pagination data for each post in the list of post pages. Here's what the div tag is going to look like after adding the v-for directive and key attribute <div v-for="post in $pagination.pages" :key="post.key"></div>.

# Using a Root Element

Since the div tag uses the v-for directive, it's going to render multiple elements. This means we cannot use it as the root element, i.e., the first element in the template tag because the template tag can only have one root element. To resolve this issue we're going to wrap the div tag that's using the v-for directive within another div tag which will serve as the root element.

# Adding the Loop

Here's what the IndexPost.vue file should now look like:

If you any questions or want to learn more about the v-for directive and the key attribute then check out these resources:

# Entry Page HTML

After updating the IndexPost.vue file with the code above, if you navigate to the entry page http://localhost:8080/posts/ (opens new window) you won't notice any changes on the page. This is because we've rendered div tags without any of the pagination data inside of them.

If you navigate to the entry page, inspect the browser, and go to the Elements tab, the HTML for the body tag should now look something like this:

Notice how the entry page consists of two div tags wrapped inside of the div tag that's being used as the root element of the template tag. Each of those div tags inside of the parent div tag corresponds to a post page that is accessible on the entry page.

# Page 2 HTML

If you navigate to the second page http://localhost:8080/posts/page/2/ (opens new window) you also won't notice any changes on the page. This is because we've rendered a div tag without any of the pagination data inside of it.

The HTML for the body tag for the second page should now look something like this:

Notice how the second page consists of one div tag wrapped inside of the div tag that's being used as the root element of the template tag. There is only one div tag since we have one post page accessible on the second page.

# Post Titles

Before we can access the post title data using the $pagination.pages property, we need to first add titles to the post files we created in the _posts directory:

  • 2020-07-03-example-page-1.md
  • 2021-11-16-example-page-2.md
  • 2022-05-08-example-page-3.md

# Adding Titles to Post Files

Since the post files are Markdown files, we can add titles to the files by adding a heading level one, e.g., # Heading Level 1 to each file which gets converted to the following HTML <h1>Heading Level 1</h1>.

Here's what the post files look like after adding the titles:

If you have any questions or want to learn more about Markdown headings, then check out the Basic Syntax (opens new window) guide.

# Adding Titles to $page Variables

When VuePress (opens new window) encounters a heading level one in a Markdown file it automatically adds a title property to the globally scoped $page variable.

The page objects in the $pagination.pages property are the same as the $page variables used by the post pages which means each page object will now have a title property that we can access in the IndexPost layout component.

You can take a look at the Global Computed (opens new window) documentation to learn more about the $page variable and other globally scoped variables.

# Displaying Post Titles

Now that we can access the post titles in the $pagination.pages property, we're ready to render the post titles on the pagination pages.

We're going to display the post titles as h2 tags which we'll be wrapping inside of two div tags. We'll be using the div tags to add styling to the list of post pages in a future tutorial.

To display the post titles we'll be using Text Interpolation (opens new window) which allows us to use variables inside of HTML tags by using the "Mustache" syntax. The "Mustache" syntax consists of wrapping a variable inside of double curly braces.

We can access the title property on each page object in our loop by using post.title. We can then display this by using the text interpolation described above.

The IndexPost.vue file should now look like this:

# Entry Page HTML

After updating the IndexPost.vue file with the code above, if you navigate to the entry page http://localhost:8080/posts/ (opens new window) you should now see the post titles being displayed with some styling provided by the default theme (opens new window).

The HTML for the body tag for the entry page should now look something like this:

# Page 2 HTML

If you navigate to the second page http://localhost:8080/posts/page/2/ (opens new window) you should now see one post title being displayed with some styling which again is being provided by the default theme (opens new window).

The HTML for the body tag for the second page should now look something like this:

# Post Previews

The post preview data is a snippet of text taken from the beginning of a post which is used as an introduction to the post in the list of post pages. Before we can access the post preview data using the $pagination.pages property, we need to first add previews to the post files we created in the _posts directory:

  • 2020-07-03-example-page-1.md
  • 2021-11-16-example-page-2.md
  • 2022-05-08-example-page-3.md

# Adding Previews to Post Files

Since the post files are Markdown files, we can use YAML (opens new window) frontmatter blocks in the files and define a custom variable preview.

Here's what the post files look like after adding the preview variables:

If you have any questions or want to learn more about YAML (opens new window) frontmatter blocks in VuePress (opens new window) then check these resources:

Number of Characters Used in Preview

The value for each post preview variable should be within a preferred minimum and maximum number of characters. This will ensure the post previews in the list of post pages looks consistent.

# Adding Previews to $page Variables

When VuePress (opens new window) encounters a YAML (opens new window) frontmatter block in a Markdown file it automatically adds each variable as a property to the globally scoped $page.frontmatter variable.

The page objects in the $pagination.pages property are the same as the $page variables used by the post pages which means each page object will now have a frontmatter.preview property that we can access in the IndexPost layout component.

You can take a look at the Global Computed (opens new window) documentation to learn more about the $page variable and other globally scoped variables.

Content Excerpt

Instead of defining the custom variable preview in the YAML (opens new window) frontmatter blocks of Markdown files, VuePress (opens new window) provides the ability to use a Content Excerpt (opens new window) by adding a <!-- more --> comment to a Markdown file. Any content above the comment gets extracted and exposed as a globally scoped $page.excerpt variable. This variable can then be used to render the list of post pages with excerpts for each post just like our custom variable preview. I prefer to use the custom variable preview because the <!-- more --> comment takes all of the content above it which includes any HTML as opposed to using just the text of a post.

# Displaying Post Previews

Now that we can access the post previews in the $pagination.pages property, we're ready to render the post previews on the pagination pages.

We're going to display the post previews as p tags which we'll place underneath the parent div tag of the h2 tag.

To display the post previews we'll be using Text Interpolation (opens new window) just like we did when displaying the post titles.

We can access the preview property on each page object in our loop by using post.frontmatter.preview.

The IndexPost.vue file should now look like this:

# Entry Page HTML

After updating the IndexPost.vue file with the code above, if you navigate to the entry page http://localhost:8080/posts/ (opens new window) you should now see the post previews being displayed with some styling provided by the default theme (opens new window).

The HTML for the body tag for the entry page should now look something like this:

# Page 2 HTML

If you navigate to the second page http://localhost:8080/posts/page/2/ (opens new window) you should now see one post preview being displayed with some styling which again is being provided by the default theme (opens new window).

The HTML for the body tag for the second page should now look something like this:

# IndexPost Styling

When viewing the pagination pages you probably noticed the list of post pages stretches across the entire width of the page. This styling doesn't look too good, so we're going to update the styling by adding the following class theme-default-content to the outermost div tag.

# Adding theme-default-content

The IndexPost.vue file should now look like this:

# theme-default-content Styles

The theme-default-content class is provided by the default theme (opens new window) and provides the following styles:

  • .theme-default-content:not(.custom) is used to select tags that have a class of theme-default-content and that don't have a class of custom by using the :not() pseudo-class. This selector is defined by the default theme (opens new window) since other pages can use the theme-default-content class along with a class of custom to apply different styles than the ones shown here. The homepage is an example of a page that uses the theme-default-content class along with a class of custom to apply different styles.
  • max-width: 740px; sets the maximum width of the div tag to be 740px. If the content is greater than the max-width, then the height of the div tag will automatically be changed. If the content is smaller than the max-width, then the max-width property has no effect.
  • margin: 0 auto; sets the margins for the div tag by setting the top and bottom margins to be 0 and the left and right margins to a value of auto. The value of auto means the browser will automatically set the left and right margins to horizontally center the div tag.
  • padding: 2rem 2.5rem; sets the padding for the div tag by setting the top and bottom paddings to be 2rem and the left and right paddings to be 2.5rem.
  • @media (max-width: 959px) will apply the styles within it when the maximum width of the display area, e.g., the browser window is less than or equal to the provided value which in this case is 959px.
  • padding: 2rem; sets the padding for all of the sides of the div tag to be 2rem when the width of the display area is less than or equal to 959px.
  • @media (max-width: 419px) will apply the styles within it when the maximum width of the display area is less than or equal to the provided value which in this case is 419px.
  • padding: 1.5rem; sets the padding for all of the sides of the div tag to be 1.5rem when the width of the display area is less than or equal to 419px.

If you have any questions about the CSS discussed above, then check out these resources:

To view these styles in the browser you can navigate to the entry page http://localhost:8080/posts/ (opens new window) or to the second page http://localhost:8080/posts/page/2/ (opens new window), inspect the browser, go to the Elements tab, locate the div tag with a class of theme-default-content, and then go to the Styles tab.

Use in the Default Theme

The theme-default-content class is used on the global Content component within the Page and Home components provided by the default theme (opens new window). If you look at the HTML on the homepage, you'll see the theme-default-content class being used on a div tag within the main tag with a class of home. We'll be discussing the global Content component, the Page component, and the Home component in more detail in future tutorials.

# IndexPost Heading

We're now going to add a heading to the pagination pages. This heading will be displayed on each pagination page.

# Displaying the Heading

We're going to display the heading as an h1 tag which we'll be adding as the first child tag of the outermost div tag.

The IndexPost.vue file should now look like this:

# Entry Page HTML

After updating the IndexPost.vue file with the code above, if you navigate to the entry page http://localhost:8080/posts/ (opens new window) you should now see the heading being displayed with some styling provided by the default theme (opens new window).

The HTML for the body tag for the entry page should now look something like this:

# Page 2 HTML

If you navigate to the second page http://localhost:8080/posts/page/2/ (opens new window) you should now see the heading being displayed with some styling which again is being provided by the default theme (opens new window).

The HTML for the body tag for the second page should now look something like this:

# Next Steps

In the next tutorial we'll be continuing the development of the IndexPost layout component by using the $pagination variable to add images to each post in the list of post pages.

Made by & for Code Monkeys 🐵