How to create a portfolio and blog using VuePress and Markdown
Tags: vue, markdown, tutorial, webdev
Published: 2020-01-16 15:57:23

---
title: How to create a portfolio and blog using VuePress and Markdown
published: true
date: 2020-01-15 14:00:20 UTC
tags: vue,markdown,tutorial,webdev
canonical_url: https://blog.logrocket.com/how-create-portfolio-blog-using-vuepress-markdown/
cover_image: https://thepracticaldev.s3.amazonaws.com/i/7mu2bxxggcoteb8zsikj.png
---
**Written by [Preetish HS](https://blog.logrocket.com/author/preetishhs/)**✏️

VuePress is a static site generator powered by Vue and developed and maintained by the Vue.js team. Although it was built with technical documentation in mind, it can be used to create so much more than that, including landing pages, portfolios, blogs, and more.

[VuePress](https://blog.logrocket.com/vuepress-in-all-its-glory-2f682e4f70c0/) comes with some default plugins out of the box, and users can also install custom plugins. The default theme is optimized for documentation, but users can also install custom themes or develop their own.

## How VuePress works

VuePress is written using Markdown, and its engine converts each Markdown file to a prerendered static HTML page. Since it is statically rendered, the application is fast and SEO-friendly. The site still uses Vue.js under the hood, meaning it is still an SPA after the initial page load, so we enjoy smooth navigation features as well.

Technically, you don’t need to know Vue.js at all to create a website using VuePress, but knowing Vue.js affords you numerous advantages. If you’re writing custom Vue components in between your Markdown files as Markdown you will find yourself limited in what you can do with VuePress.

## Installing and using VuePress

To install VuePress, enter the following code.

```jsx
yarn global add vuepress
```

Note: at the time of writing, I wasn’t able to install VuePress using NPM.

Next, create a directory for your VuePress app and create a starter file. Let’s try to build a simple portfolio with a blog using VuePress.

Start the application by running the following.

```jsx
vuepress dev
```

Open the browser `localhost:8080`, and you’ll see an application running with just a single Markdown file. Remember, the `README.md` file is converted to `index.html`. If we had used some other name (say, `Hello.md` ), we would’ve had to open `localhost:8080/hello`.

`vuepress dev` causes VuePress to run in the root directory. We’ll create an `src` or `docs` directory to keep all our files, then use:

```jsx
vuepress dev src
```

## Directory structure

![VuePress Directory Structure](https://i1.wp.com/blog.logrocket.com/wp-content/uploads/2020/01/vuepress-directory-structure.png?resize=930%2C692&ssl=1)

This is roughly what the [directory structure](https://v1.vuepress.vuejs.org/guide/directory-structure.html) of a VuePress app looks like. We’ll go over each of these directories and their uses in detail.

[![LogRocket Free Trial Banner](https://i0.wp.com/blog.logrocket.com/wp-content/uploads/2017/03/f760c-1gpjapknnuyhu8esa3z0jga.png?resize=1200%2C280&ssl=1)](https://logrocket.com/signup/)

### Page routing

The routing table is created automatically based on the directory structure. The root `README.md` file will be our main `index.html`, and subsequent directories inside the root will take the URL pathname as their directory names.

Before we get too deep in the weeds, let’s create a few more files to see the routing in action.

Create a directory called `src` and move the `README.md` file into this folder. Create one more file called `Article.md` in the `src` directory and add the following Markdown code.

```jsx
//Article.md
# My first Article
VuePress is an amazing static site generator! 
```

Now rerun the project using the following.

```jsx
vuepress dev src
```

Open the browser, and you’ll see `localhost:8080/article`, as well as the first article. But our website looks a little plain without anything in the header, so let’s configure that now.

### Configuration

VuePress is highly customizable. Let’s create a `.vuepress` directory inside the `src` folder and a `config.js` file inside that. The `config.js` file is used to define all the global configurations, such as the main title, theme config, webpack config, adding plugins, and more.

```jsx
//config.js
module.exports = {
  title: 'Preetish HS',
  description: 'My awesome portfolio blog'
}
```

Save and reload the application, and you’ll see the title displayed in the header. Let’s do some more theme-specific modifications in this file.

```jsx
//config.js
module.exports = {
  title: 'Preetish HS',
  description: 'My awesome portfolio blog',
  themeConfig: {
    nav: [
      { text: 'Home', link: '/' },
      { text: 'Article', link: '/article' }
    ]
  }
}
```

We just added two `navbar` links, which should look like this:

![VuePress Navbar Links](https://i2.wp.com/blog.logrocket.com/wp-content/uploads/2020/01/vuepress-navbar-links.png?resize=730%2C223&ssl=1)

Before we customize our app any further, let’s add two more articles. Create `Article2.md` and `Article3.md`. The default theme supports all basic Markdown features, such as headers, tables, and syntax highlighting for code blocks.

```jsx
//Article2.md
# My second article

VuePress has been great so far. We were able to create pages and customize the theme.
```

![VuePress Second Article Page](https://i2.wp.com/blog.logrocket.com/wp-content/uploads/2020/01/vuepress-second-article.png?resize=730%2C329&ssl=1)  
Add some content to create a third article. Since the number of articles is increasing, let’s put all the articles into a `blog` folder and create a separate page for them.

Change the `config.js` to link to the `blog`.

```jsx
 themeConfig: {
    nav: [
      { text: 'Home', link: '/' },
      { text: 'blog', link: '/blog' }
    ]
  },
```

Clicking on the `blog` link on the navbar will result in a 404 page because there is no `Readme.md` file inside the `blog` folder, but the articles are still accessible from `localhost:8080/blog/article`.

Let’s design a page to showcase all our articles. We could build something like this:

```jsx
//blog/Readme.md

# My Posts
[Article 1](Article.md)
[Article 2](Article2.md)
[Article 3](Article3.md)
```

This would work, but it would look like something straight out of the 90s. Instead, let’s take advantage of Vue.js and write a beautiful card layout for our blog page.

## Using Vue components in Markdown

We can create Vue components inside the `.vuepress/components` directory. Any `.vue` files in this folder will be registered globally and accessible from any of our Markdown files by directly using the component name.

Let’s create a `Posts.vue` component with some cards.

```jsx
<template>
  <div class="post-container">
    <router-link to="article">
      <div class="post-card">
        <img class="article-image" src="../public/images/blog_hero_image.jpg" />
        <div class="description">
          My first Article
        </div>
      </div>
    </router-link>
    <router-link to="article2">
      <div class="post-card">
        <img class="article-image" src="../public/images/blog_hero_image.jpg" />
        <div class="description">
          My Second Article
        </div>
      </div>
    </router-link>
    <router-link to="article3">
      <div class="post-card">
        <img class="article-image" src="../public/images/blog_hero_image.jpg" />
        <div class="description">
          My third Article
        </div>
      </div>
    </router-link>
  </div>
</template>
<script>
export default {}
</script>
<style scoped>
.post-container {
  display: flex;
  flex-wrap: wrap;
  width: 100%;
}
.post-card {
  width: 600px;
  height: 150px;
  margin: 10px;
  border: 1px solid #ccc;
  border-radius: 3px;
  padding: 10px;
  display: flex;
  align-items: center;
}
.article-image {
  height: 100%;
}
.description {
  width: 100%;
  display: flex;
  justify-content: center;
}
</style>
```

Use the below component in the Markdown file and reload the page.

```jsx
# My Posts
<posts />
```

![Posts Page Created With VuePress](https://i1.wp.com/blog.logrocket.com/wp-content/uploads/2020/01/vuepress-posts-component.png?resize=842%2C804&ssl=1)

This looks much better, but still not perfect. We are hardcoding everything, and a blog post card should have more details, such as the author name and a short description.

VuePress injects the whole site’s data into all the components. This enables us to access `$site` and `$page`, which consists of site- and page-related data in our Vue components and Markdown files.

`$site` data would look something like this:

![VuePress Site Data](https://i2.wp.com/blog.logrocket.com/wp-content/uploads/2020/01/vuepress-site-data-example.png?resize=730%2C194&ssl=1)

This includes data related to both the complete application configuration and individual pages. We use `$site.pages` to create our posts page dynamically instead of hardcoding it. The problem is that this array lists all the pages, including the homepage and the posts page itself. We need to differentiate the article pages from other pages.

## Frontmatter

In each page object, you’ll see an object called `frontmatter`. The `frontmatter` is essentially page-specific metadata, which we can write in YAML, JSON, or TOML format at the beginning of the file. Let’s write `frontmatter` for our article pages.

```jsx
//Article.md

---
title: My first article
description: Basic intro to vuepress
author: Preetish HS
type: article
---
```

Now we can use these to dynamically populate our posts page.

```jsx
<template>
  <div class="post-container">
    <router-link v-for="page in pages" :to="page.path">
      <div class="post-card">
        <img class="article-image" src="../public/images/blog_hero_image.jpg" />
        <div class="page-detail">
          <div class="page-title">{{ page.title }}</div>
          <div class="page-description">{{ page.frontmatter.description }}</div>
          <div class="page-author">Author: {{ page.frontmatter.author }}</div>
        </div>
      </div>
    </router-link>
  </div>
</template>
<script>
export default {
  data() {
    return {
      pages: []
    }
  },
  mounted() {
    this.$site.pages.forEach(page => {
      if (page.frontmatter.type === 'article') {
        this.pages.push(page)
      }
    })
  }
}
</script>
```

![VuePress Posts Page Dynamically Populated with Frontmatter](https://i0.wp.com/blog.logrocket.com/wp-content/uploads/2020/01/vuepress-posts-page-frontmatter.png?resize=811%2C811&ssl=1)

The code looks much cleaner, and the cards look better too.

Now let’s create another folder for `portfolio`, add each of our projects, and, finally, add a link to the navbar.

## Homepage customization

Until now, we haven’t done much to the homepage. We can customize VuePress’ default theme to create a stunning homepage, complete with banner images and more. Open the `Readme.md` from the root directory and add the following `frontmatter` code.

```jsx
---
home: true
heroImage: /images/banner.jpg
heroText: Hi, I am Preetish. Welcome to my blog!
tagline: I am a Remote Vue.js Developer and a Tech Blogger
actionText: See my work →
actionLink: /blog/
features:
  - title: Frontend Developer
    details: I am a frontend developer with expertise in Vue.js and Nuxt.js
  - title: Tech Blogger
    details: I love writing tech articles to share my knowledge on frontend frameworks, libraries, best practices and more.
  - title: Traveller
    details: Travelling is my favourite activity, I am an avid hiker and backpacker
footer: Developed using VuePress ♥️
---
```

We added a lot of things, such as headers, a banner image, and a few other details, to our portfolio homepage. It should now look something like this:

![Blog Homepage Customized With VuePress](https://i1.wp.com/blog.logrocket.com/wp-content/uploads/2020/01/vuepress-customized-homepage.png?resize=1071%2C870&ssl=1)

You might recognize this layout from the official Vue.js documentation — VuePress was initially written to maintain Vue.js and other official Vue package documents — but we can still customize it further. We can override default CSS, write custom CSS or even write custom layouts for the entire website.

### Custom CSS

To write custom, page-specific CSS, add a class name to the page using `frontmatter` and write styles under that class to override the existing ones.

Let’s customize our homepage to make it look a little better. Add the below code in the `Readme.md` of the root directory.

```jsx
---
pageClass: homepage
---
```

We need to write the styles in the `.vuepress/styles/index.styl` file in `stylus` format. Let’s create a file there and make some changes to the homepage.

```jsx
.theme-container.homepage {
  .hero {
    img {
      width: 100%;
      object-fit: cover;
      margin: 30px 0 0 0;
    }
  }
  .home {
    max-width: 100%;
    padding: 0;
  }
  .features {
    padding: 0.5rem 1rem;
  }
}
```

With the above code, we added some custom CSS to make our page and hero image full-width. We can also override the default color scheme, change the height of the navbar, or even modify the responsive breakpoints of the default theme.

Let’s modify the main color and change it to teal for now. This code is written in the `.vuepress/styles/palette.styl` file:

```jsx
$accentColor = #007f80
```

![Blog Homepage Styled With Custom CSS](https://i0.wp.com/blog.logrocket.com/wp-content/uploads/2020/01/vuepress-homepage-custom-css.png?resize=730%2C354&ssl=1)

Now our portfolio website is shaping up!

## Installing plugins

Create a `package.json` file by running:

```jsx
npm init
```

VuePress has many official plugins as well as countless community plugins. Let’s install a plugin that enables readers to share an article on social media.

```jsx
npm install vuepress-plugin-social-share -D
```

To use the plugin, we need to declare it in the `config.js` file with additional configurations, if required.

```jsx
//config.js
module.exports = {
  plugins: [
      'social-share',
      {
         networks: ['twitter', 'facebook', 'reddit', 'telegram']
      },
  ],
}
```

Now let’s apply the plugin to our article.

```jsx
//Article2.md
.
.
<social-share :networks="['facebook', 'twitter']"/>
```

![Adding Plugins to the Article Page With VuePress](https://i1.wp.com/blog.logrocket.com/wp-content/uploads/2020/01/vuepress-article-plugin.png?resize=1038%2C434&ssl=1)

## More customizations

It may be helpful to rename each of our article files to something meaningful (e.g., `my-first-article.md`) to generate a more SEO-friendly URL (e.g., `http://localhost:8080/blog/my-first-article.html`).

When we open an individual article, we see a lot of space on the sides. Also, we cannot navigate to other articles directly. Let’s fix this by making use of the `siderbar` feature in the default theme.

In the `config.js` file, add the following code.

```jsx
themeConfig: {
    sidebar: [
      '/blog/my-first-article',
      '/blog/my-second-article',
      '/blog/my-third-article'
    ]
}
```

![Customizing the Article Page With CSS](https://i0.wp.com/blog.logrocket.com/wp-content/uploads/2020/01/vuepress-article-css-customization.png?resize=1650%2C922&ssl=1)

This should create a sidebar for navigation as well as links to navigate to the previous and next article.

## Deployment

Finally, let’s build and deploy our application by running:

```jsx
vuepress run build src
```

Build files will be available in `.vuepress/dist`. Similar to `Nuxt generate`, VuePress creates HTML static pages for all possible routes. This can be deployed easily in any static hosting service, such as GitHub Pages, [Netlify](https://dev.to/bnevilleoneill/exploring-netlify-split-testing-3ndn), etc.

## Conclusion

Now you should be able to build an SEO-friendly static portfolio and blog using VuePress in about 10 minutes. As you can see, you can use VuePress even without knowledge of Vue. The default theme has myriad features out of the box, and you can customize it to any extent by modifying the existing theme or writing a new theme altogether from scratch. VuePress is powerful, and its use should not be limited to documentation.

* * *
## Experience your Vue apps exactly how a user does 

Debugging Vue.js applications can be difficult, especially when there are dozens, if not hundreds of mutations during a user session. If you’re interested in monitoring and tracking Vue mutations for all of your users in production, [try LogRocket.](https://www2.logrocket.com/vue-performance-monitoring)

![Alt Text](https://thepracticaldev.s3.amazonaws.com/i/t4ni6gomxqbkbxygmco0.png)

[LogRocket](https://www2.logrocket.com/vue-performance-monitoring) is like a DVR for web apps, recording literally everything that happens in your Vue apps including network requests, JavaScript errors, performance problems, and much more. Instead of guessing why problems happen, you can aggregate and report on what state your application was in when an issue occurred.

The LogRocket Vuex plugin logs Vuex mutations to the LogRocket console, giving you context around what led to an error, and what state the application was in when an issue occurred.

Modernize how you debug your Vue apps - [Start monitoring for free.](https://www2.logrocket.com/vue-performance-monitoring)
* * *

The post [How to create a portfolio and blog using VuePress and Markdown](https://blog.logrocket.com/how-create-portfolio-blog-using-vuepress-markdown/) appeared first on [LogRocket Blog](https://blog.logrocket.com).