DEV Community

Sergey Khomushin
Sergey Khomushin

Posted on • Updated on

Build your best website on VuePress

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

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 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:

$ vuepress eject src
Enter fullscreen mode Exit fullscreen mode

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:

node_modules
src
|
- .vuepress
- contact-us
  |- README.md
- docs
  |- README.md
- faq
  |- README.md
- pricing
  |- README.md
- README.md
package.json
Enter fullscreen mode Exit fullscreen mode

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

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

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:

<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>
Enter fullscreen mode Exit fullscreen mode

Now we just need to declare a landingPage parameter for every markdown file: I use the JSON frontmatter:

---
{
    "landingPage": "Home"
}
---
Enter fullscreen mode Exit fullscreen mode

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.

$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
Enter fullscreen mode Exit fullscreen mode

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, but from my point of view, it's better to set all configs in config.js. We can ask the context for our siteConfig:

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

Enter fullscreen mode Exit fullscreen mode

config.js:

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

Enter fullscreen mode Exit fullscreen mode

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 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.

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

And also you can use the ClientOnly wrapper:

<ClientOnly>
    <CookiesBar />
</ClientOnly>
Enter fullscreen mode Exit fullscreen mode

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:

created() {
    if (typeof this.$ssrContext !== 'undefined') {
        let script = '<script type="application/ld+json">{}&lt;/script>';
        this.$ssrContext.userHeadTags += script.replace('&lt;', '<');
    }
}
Enter fullscreen mode Exit fullscreen mode

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.

$ vuepress build src -d dist --no-cache
Enter fullscreen mode Exit fullscreen mode

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.

Top comments (0)