Back to main page Traveling Coderman

Showing Plausible view counts in an Eleventy blog

130 views

So you have your statically generated Eleventy blog running, Plausible Analytics setup to track its traffic and now want to show and utilize the number of views of your blog posts? In this post, I show you how to get the page views from Plausible into your blog while keeping everything nicely statically generated at build time.

Steps ๐Ÿ”—

  1. Retrieve and provide Plausible stats as Eleventy global data
  2. Plausible API authentication
  3. Setup a daily build
  4. Show page views on posts
  5. Sort posts with page views

Tradeoffs ๐Ÿ”—

There are multiple architectural approaches to integrate the page views.

  • Retrieve page views from Plausible in the browser
    • Pros User sees latest data
    • Cons Requires client-side JavaScript, runtime dependency on Plausible API
  • Retrieve and cache page views (as JSON in git) in a scheduled job
    • Pros Build and deploy process independent of Plausible API
    • Cons Less frequently updated data, build command acts on cache
  • Retrieve and cache page views before deployment
    • Pros Latest page views on deployment
    • Cons Deployment depends on Plausible API availability
  • Retrieve page views before each build
    • Pros Latest page views on deployment, latest page views locally
    • Cons Deployment and local development depends on Plausible API availability

In this post I am presenting the retrieval of page views before each build. Additionally, we setup a daily build. This way, page views update also if there is no manual deployment.

Retrieve and provide Plausible stats as Eleventy global data ๐Ÿ”—

Eleventy allows to compute data for the templates dynamically. We create a global data file _data/pageStats.js. The exported value from this module is made available in the templates via <post>.data.pageStats. We want this global variable pageStats to be a map from page URLs to the page stats.

{
"/posts/post-1": {
"pageviews": 1784
},
"/posts/node/post-3": {
"pageviews": 8463
}
}

Hence, in the file pageStats.js, we first retrieve the page stats and then format them.

const axios = require("axios");

module.exports = async () => {
const response = await requestPagestats();
return formatResponse(response);
};

function requestPagestats() {
// ...
}

function formatResponse(response) {
// ...
}

In the function requestPagestats, we use axios to request the page stats. The Plausible API endpoint /stats/breakdown returns statistics per page.

We pass it several parameters:

  1. The site_id is the domain of your site.
  2. The period should ideally be all, but is limited to 12mo maximum currently in the API.
  3. The metrics is a comma-separated list. We are interested in the pageviews.
  4. The property allows us to specify based on what to breakdown the stats. In our case, this is the page url event:page.
  5. The filters allow us to only retrieve posts by matching the URL.
  6. The Authorization header contains a Bearer authentication token which we setup in the next section.
function requestPagestats() {
return axios.get(`https://plausible.io/api/v1/stats/breakdown`, {
params: {
site_id: "traveling-coderman.net",
period: "12mo",
metrics: "pageviews",
property: "event:page",
filters: "event:page==/posts/**/*",
},
headers: {
Authorization: `Bearer ${process.env.PLAUSIBLE_TOKEN}`,
},
});
}

The response of the Plausible API is a list of the post URLs with their page views.

{
"results": [
{
"page": "/posts/post-1",
"pageviews": 1784
},
{
"page": "/posts/node/post-3",
"pageviews": 8463
}
]
}

We transform the Plausible API response into our desired format.

function formatResponse(response) {
return response.data.results.reduce(
(acc, page) => ({
...acc,
[page.page]: {
pageviews: page.pageviews,
},
}),
{}
);
}

Plausible API authentication ๐Ÿ”—

In the Plausible settings, you can setup API tokens.

The API token section in the Plausible settings

I recommend to setup two distinct tokens for local development and your production deployment pipeline. This way, you can revoke them independently in case of a security incident. If you work in a team, then setup tokens for each engineer.

Note I am hosting on Netlify. The retrieval of the page stats works independently of the hosting provider. You just need to make sure to make the environment variable PLAUSIBLE_TOKEN accessible to the Eleventy build command.

Passing the token locally ๐Ÿ”—

We use dotenv to provide the token locally to the Eleventy build.

npm install --save-dev dotenv

We create a file .env with the environment variable PLAUSIBLE_TOKEN. Replace <...> with your created development token.

PLAUSIBLE_TOKEN=<...>

We exclude the file .env from being committed in git via the .gitignore.

// ...
.env

In the Eleventy config .eleventy.js, we load dotenv after the imports but before any code execution.

// ... imports ...

require("dotenv").config();

module.exports = function (eleventyConfig) {
// ...
};

Passing the token in the deployment ๐Ÿ”—

This step is specific to the hosting provider. In Netlify, you can configure an environment variable to be passed to each deployment of your site. Use the created Plausible production API token.

The environment variable section within the Netlify settings

Setup a daily build ๐Ÿ”—

We want the latest daily page stats to be visible on the website.

This step is also specific to the hosting provider. In Netlify, there are currently no scheduled builds. However, Netlify offers build hooks.

The build hooks section within the Netlify settings

Once we create a build hook, we can pass it to external tools to call it on a schedule. There are multiple options available to achieve this. My repository is hosted on GitHub. Hence, I am using GitHub Actions to avoid the introduction of a new third-party dependency.

In the folder .github/workflows, we create a file daily-netlify-build.yml.

name: Daily Netlify Build

on:
schedule:
# Run at 15:30 daily
- cron: "30 15 * * *"
workflow_dispatch:

jobs:
build:
name: Daily Netlify Build
runs-on: ubuntu-latest
steps:
- name: Call Netlify build hook
run: curl -X POST -d {} $

This workflow is executing each day at 15:30 and calls the Netlify build hook. Additionally, we add the option workflow_dispatch. This allows for testing of this workflow without waiting for 15:30.

Since the build hook URL is sufficient to authenticate, we store it as a secret in GitHub.

The secrets section within the GitHub settings

Show page views on posts ๐Ÿ”—

With the previous steps, the pageStats variable is available in templates. From this point, you can use it in pretty much any way you like. Probably the most common scenario is to show the page views on the individual posts.

The rendered page views with eye icon, number of views and the text 'views'

In the individual post template, we access and render the page views.

{% if pageStats[page.url].pageviews %}
<span class="pageviews">
<i class="fas fa-eye" aria-hidden="true"></i>
<a
href="https://plausible.io/traveling-coderman.net?page={{ page.url }}&period=all"
target="_blank"
>

{{ pageStats[page.url].pageviews if pageStats[page.url].pageviews
>= 100 else '<100' }} views
<i class="fas fa-external-link-alt"></i>
</a>
</span>
{% endif %}

In the variable pageStats, we can lookup the page stats with the URL of the page page.url. Since the page might be new and not be present in the Plausible stats, we check if the value is set.

Font Awesome provides an eye icon . We add aria-hidden="true" since the word views describes the span.

We link to the specific Plausible stats of the page https://plausible.io/<your_domain>?page={{ page.url }}&period=all.

Note While the period all exists in this link, it is not supported in the Plausible API at the time of publishing this post.

We show the page views with an interpolation {{ pageStats[page.url].pageviews if pageStats[page.url].pageviews >= 100 else '<100' }}. This leads to the page views being shown as <100 if there are less than 100 views.

The Font Awesome icon fa-external-link-alt indicates the external link. With a bit of CSS, we limit it to be shown on hover.

.pageviews .fa-external-link-alt {
display: none;
}

.pageviews a:hover .fa-external-link-alt {
display: inline-block;
}

Sort posts with page views ๐Ÿ”—

Besides showing the number of page views, we can use them to define the order of displayed posts.

In the Eleventy config .eleventy.js, we can create new collections with custom sorting. I am using a tag post on each template file that represents a post. Additionally, I use other tags to further distinguish posts into "categories".

module.exports = function (eleventyConfig) {
const tags = ["post", "coding", "process" /* ... */];
for (const tag of tags) {
eleventyConfig.addCollection(`${tag}-popular`, (collectionApi) =>
collectionApi.getFilteredByTag(tag).sort(byPageviews)
);
}
};

The function byPageviews compares two posts according to their number of page views.

function byPageviews(post1, post2) {
return (
post2.data.pageStats[post2.url]?.pageviews -
post1.data.pageStats[post1.url]?.pageviews
);
}

Note While the page stats pageStats are defined globally, they are also available to each post in the object data. The way we defined it, post1.data.pageStats is equal to post2.data.pageStats.

We can use these collections in templates.

{% for post in collections["post-popular"] %}
<!-- ... -->
{% endif %}

Conclusion ๐Ÿ”—

We setup the retrieval of Plausible API page stats into our Eleventy build. These stats are retrieved locally and on deployment builds. We setup a daily build such that the production site shows recent data. We rendered the page views on each post and sorted posts according to their page views.