Build your best website on VuePress
Tags: vue, vuepress, beginners, todayilearned
Published: 2019-09-30 14:51:48

At this moment, there are a large number of different platforms for creating universal applications, and today I'm going to talk about one of them: [VuePress](https://vuepress.vuejs.org/)

### What is VuePress?
First of all, let me quote the documentation:

> VuePress is a static site generator... ...Each page generated by VuePress has its own pre-rendered static HTML, providing great loading performance and is SEO-friendly. Yet, once the page is loaded, Vue takes over the static content and turns it into a full Single-Page Application (SPA). Extra pages are fetched on demand as the user navigates around the site.

In simple words, we have a modern generator to create a universal application with the built-in Vue framework.

> VuePress is focused on content-centric static sites and provides features tailored for technical documentation out of the box.

And this generator uses built-in markdown for documentation, which allows you to create perfect docs for your product or service in an easy way! That is absolutely awesome, isn't it?

### Commercial break
Before we start learning about various VuePress issues, let me introduce [EmailJS](https://www.emailjs.com/) first, whose website was created on the basis of VuePress. This is exactly why I'm using EmailJS site as an example for this article.

EmailJS service allows you to send emails from javascript with only a couple of clicks! It's definitely a must-have service for every web developer! 

### To be or not to be
The first thing we see after starting the VuePress project by default, the design is not suitable for a professional solution. And the problem is not the color theme at all. 


#### Customize
To fully customize the theme, we are going to eject the default theme into src folder:

```sh
$ vuepress eject src
```

When the ejection process is done, we can see .vuepress folder with default theme, configs and components.

#### Architecting

Our website has 5 major sections, which you can find in our navigation panel: Homepage, Faq, Docs, Pricing and Contact Us. Our links depend on folder structures, so let's create these folders in src folder and markdown files for each section:

```txt
node_modules
src
|
- .vuepress
- contact-us
  |- README.md
- docs
  |- README.md
- faq
  |- README.md
- pricing
  |- README.md
- README.md
package.json
```

Now we need to add these sections to our navbar. We can use a VuePress config file: .vuepress -> config.js

```js
...
themeConfig: {
    nav: [{
        text: 'Home',
        link: '/'
    }, {
        text: 'FAQ',
        link: '/faq/'
    }, {
        text: 'Docs',
        link: '/docs/'
    }, {
        text: 'Pricing',
        link: '/pricing/'
    }, {
        text: 'Contact Us',
        link: '/contact-us/'
    }]
...
```

#### Pages
Next, run our project and test our links in navbar and you will find everything works except for... distinctive designs and contents are in need.
In order to accomplish that, we can create a pages directory in the theme folder, and implement vue components: ContactUs.vue, Home.vue and etc.

> I recommend to move the Page.vue from components to pages. We can use it for our docs section.

And then inject our pages by Vue `:is` to load dynamic components:

```vue
<template>
    <section>
        <component :is="landingPage"/>
    </section>
</template>

<script>
    import Page from '@theme/pages/Page.vue';
    import Home from '@theme/pages/Home.vue';
    import FAQ from '@theme/pages/FAQ.vue';
    import Pricing from '@theme/pages/Pricing.vue';
    import ContactUs from '@theme/pages/ContactUs.vue';

    export default {
        components: {Home, Page, FAQ, Pricing, ContactUs},

        computed: {
            landingPage() {
                return this.$page.frontmatter.landingPage || 'Page';
            }
        }
    }
</script>
```

Now we just need to declare a landingPage parameter for every markdown file: I use the JSON [frontmatter](https://v1.vuepress.vuejs.org/guide/frontmatter.html):
```md
---
{
    "landingPage": "Home"
}
---
```

#### Palette
To override the style of the default theme or define some color variables, we need to create a `palette.styl` file in the [styles directory](https://vuepress.vuejs.org/config/#styling).

```scss
$accentColor = #FCA253; // primary color
$nprogressColor = #FCA253; // progress bar color
$borderColor = rgba(0, 0, 0, 0.1); // search box, navbar and etc
$textColor = #2C3E50; // main text color
$arrowBgColor = #FCA253; // arrows color in sidebar
```

#### Search box
The built-in search engine is looking for all headers in markdown files, but we need it only for our docs section. So how are we going to fix it?

We can set it in [index.js](https://vuepress.vuejs.org/plugin/official/plugin-search.html), but from my point of view, it's better to set all configs in config.js. We can ask the context for our siteConfig:

```js
// Theme API.
module.exports = (options, ctx) => ({
    plugins: [
        ['@vuepress/search', ctx.siteConfig.searchBox]
    ]
});

```

config.js:
```js
module.exports = {
    searchBox: {
        test: '/docs'
    }
};

```

Various plugins can be added to our site exactly the same way. For instance, the Progressive Web App plugin, GA and etc.

#### Scroll listener: window doesn't exist?
[VuePress](https://vuepress.vuejs.org/) uses server side to generate HTML files, therefore we can't use window object in `create`, `destroy` and `computed` (!!!) lifecycle methods. However, we can do it in `methods`, `beforeMount` and `beforeDestroy`.

```js
export default {
    methods: {
        handleScroll() {
            this.isSticky = window.pageYOffset < 0;
        }
    },
    beforeMount() {
        window.addEventListener('scroll', this.handleScroll);
    },
    beforeDestroy() {
        window.removeEventListener('scroll', this.handleScroll);
    }
}
```

And also you can use the `ClientOnly` wrapper:
```html
<ClientOnly>
    <CookiesBar />
</ClientOnly>
```

#### Script per page
It's pretty easy to add meta tags per page, but what about scripts? Well, it's complicate.
As we mentioned above about server side build, we can inject our code in `created` method and check if it's server side process:
```js
created() {
    if (typeof this.$ssrContext !== 'undefined') {
        let script = '<script type="application/ld+json">{}&lt;/script>';
        this.$ssrContext.userHeadTags += script.replace('&lt;', '<');
    }
}
```

> Vue crashes when it spots a `</script>`. Maybe there is a better solution, but we can work it around without using the tag script and replace `&lt;` to `<` during build time.

#### Build
By default, VuePress builds the code into .vuepress directory, which is an absolute nuisance! Besides, it's also recommended to disable the cache for the build process.

```sh
$ vuepress build src -d dist --no-cache
```

### Conclusion
We've learnt about VuePress, defined the initial architecture for our project, and also created Vue components for each individual page.
There's no doubt that it's one of the best solutions to use VuePress for creating a site with structured documentation. I'll be glad to know if you find my solution is valuable and more efficient.
If there's any question on certain topic regarding VuePress, please make a comment below.

Have a great day!

> Please note that version 1.0.2 was used. Perhaps you will find many issues have been resolved in the new version.