This is the public repository for tota.sk—an NPO in Slovakia cherishing the Rusyn culture.
I wanted to make this repo public to share few examples of how I used:
- Nuxt.js—the Vue.js framework to build statically generated website and single page app
- Kentico Kontent—the headless CMS to manage the content
- Build setup
- Examples of use
- Rendering the project detail (Rendering dynamic pages of one type)
- Rendering Rich-text components (as in Kentico Kontent components)
- Custom list of projects
- Combining Single-page application (SPA) and server-side rendered content
- Generating static site and sitemap
- Adding Cookie Consent
- Schema.org metadata
- Lazy-loading images
- Special thanks
- License
If you have any questions about the code or examples, feel free to create an issue.
# install dependencies
$ npm ci
# serve with hot reload at localhost:3000
$ npm run dev
# build for production and launch server
$ npm run build
$ npm run start
# generate static project
$ npm run generate
For detailed explanation on how things work, check out Nuxt.js docs.
Each project detail has its own page. Review this section, if you’d like to render dynamic pages of the same type (e.g. articles, blog posts,…).
All projects are stored as a structured content in Kentico Kontent. We’ll get data as a structured JSON via Kentico Kontent Delivery API and “load” it to a Vuex Store. Here’s how it’s done for project detail:
- store for projects → store/project.js
- project detail is in “project” state with corresponding mutation/action
- using kentico-kontent-nuxt-module to get content via Delivery API. Check out module’s documentation and how it is configured for this repository.
The template for project detail is located at pages/projekt/_id.vue. Nuxt has a convention how to name/categorize page templates to generate routes in a desired way (in my case I was looking for routes tota.sk/projekt/{project-detail}.
Here are extra notes on what is happening inside the file:
- using pug templating instead of HTML
- fetching project detail asynchronously
- loading project’s state via Vue’s computed property
- head() cannot access computed project state in a template, so we’re accessing the store directly to fill in project’s meta data
- project’s content is stored as a Rich-text with couple of nested components. We’ll cover the rendering of Rich-text in the following example.
In Kentico Kontent, you can extend Rich-text editor functionality with custom inline Components. With Components, you’ll both create more appealing content for visitors and you’ll still get nicely structured response from Delivery API.
I use components a lot. If you take a look at this project, there couple of Components within the Rich-text: Image with Caption, YouTube Video, Point-of-sale list, Publication Info. (Here’s the list of all used Rich-text components).
There are couple of ways, how you can go about rendering the components, we’ll cover the two ways we tried.
kentico-kontent-nuxt-module is “wrapping” kontent-delivery-sdk-js and gives you tools to ease the work with JS SDK in Nuxt.js app. With this toolset, you need to:
- Write a resolver for each Rich-text component (or linked item) and save it models/ folder in Nuxt.js project; Example of resolver in Kontent Delivery JS SDK
- Register a resolver with kentico-kontent-nuxt-module, as it is shown in this example
We have tried this approach at first, and it was fine; we were able to write all Rich-text components this way. What we didn’t like so much with this approach was that with JS SDK you have to write resolvers as interpolated string. This way, content-heavy components got less maintainable templates. And on top of that we weren’t using Vue’s native component approach (yeah, we wanted to marry Vue components with Kentico Kontent components). So we rewrote resolvers into Vue components the following way:
@vit-svoboda wrote kontent-rich-text-for-vue—an NPM package to help you resolve/render Kontent’s rich-text components as Vue components. Let me walk you through how to render components. I’ll use “Project” content type as an example as it includes a lot of components:
- set rich_text_components in project store
- write a Rich text component that you want to be rendered (take an inspiration from the list of Rich-text components in this project)
- write a Vue component that will keep the list of all your Rich text components to be rendered. Here's mine—rich-text-component.vue.
- use it all in Project Vue template:
- import { RichText } from 'kontent-rich-text-for-vue' and use it in a
<template>
section - in
<script>
section take a look at: componets, richTextComponent, provide ()
- import { RichText } from 'kontent-rich-text-for-vue' and use it in a
There is a list of projects at homepage. These projects don’t have any automatic ordering (e.g. sort by date desc), but they’re custom-ordered based on agency’s preferences.
In Kentico Kontent, I’ve created a “Project list” content type (see its definition). The “Project list” has one “Linked items” element element accepting only the “Project” content type. This way you can update the order of the project anytime in Kentico Kontent.
All projects are stored as a structured content in Kentico Kontent. We’ll get data as a structured JSON via Kentico Kontent Delivery API and “load” it to a Vuex Store. Here’s how it’s done for project detail:
- store for projects → store/project.js
- project detail is in “projects” state with corresponding mutation/action
- using kentico-kontent-nuxt-module to get content via Delivery API. Check out module’s documentation and how it is configured for this repository.
Vue component is located at pages/components/projects-list.vue; as a component it’s used at pages/index.vue
Almost all the projects at tota.sk were offline projects (e.g. this book). But one project—translit—is different. It’s an online project, a single page application to transliterate Rusyn language from Cyrillic script to Latin alphabet and vice versa.
Here’s the thing. By default, Nuxt.js can work in one of the two modes—as a Universal SSR (Server-side rendering) or an SPA (single page application). You have to choose one mode and I’ve chosen Universal SSR (for SEO reasons + I wanted to generate a static site).
I though it may be an issue if I’d like to run client-side JavaScript while the whole repo is in Server-side Rendering (SSR) mode. Fortunately, the Nuxt has a <client-only>
component that let’s you wrap another component that is supposed to be executed at client side.
Take a look at how it’s used in code:
- translit as a page is at pages/translit.vue
- within the file, there is translit-app component that is wrapped in [
<client-only>
] component (fyi I’m using pug templating, so there are no<>
html brackets) - translit-app component therefore works as a “partial SPA”.
- within the file, there is translit-app component that is wrapped in [
By the way, if you’d ever need a transliteration script, it’s also available on GitHub as an open-source NPM package—translit.
When you’re running Nuxt.js in Universal SSR mode, generating a static site is easy—you call npm run generate
. You will get a static HTML file for each page you have. However, the static generation is not including dynamic pages by default.
In this project, majority of content are dynamic pages (projects and articles, in particular). If I wanted to generate static files of them, I had to include the routes in nuxt.config.js
file. Here’s the official documentation and here’s how I adjusted my nuxt.config.js
file. There are two promises calling for a list of projects and for a list of articles.
I have used @nuxtjs/sitemap package to auto-generate a Sitemap. This module will generate sitemap.xml for those pages that are known, i.e. not for the dynamic pages. But if you set the routes as we already did to generate a static site, you are all set.
By the way, the module is rather robust and I’m only using a portion of it. However, if you’d like to take a look at my config, take a look at nuxt.config.js
file.
I have decided to use the cookieconsent NPM package. It seemed to have a lot configuration options. However, the JavaScript configuration has to be executed at client side (I have talked about Server vs. Client server rendering elsewhere).
Here’s the documentation of how you can run client-side JS in SSR mode. Here’s how I have included the configuration in the master template.
- For Book metadata check out book-info.vue Vue component.
- Here’s the Google’s Structured Data Testing Tool to check you’ve applied schema properly (example—one of the books)
- Used vue-lazyload plugin
- With the help of this tutorial
- My implementation in this commit
@vit-svoboda for making it happen. He helped me a lot with JavaScript + he has done done an awesome Vue module for parsing Kentico Kontent’s Rich-text components.
Feel free to get inspired by the code snippets. All other content (written content and logotypes related to tota agentura) are copyrighted.