Volg hoeveel mensen je artikelen lezen, met Plausible.io, Vue.js en Azure functions

Jeroen Bach

Jeroen Bach · Linkedin

5 min read ·

In het vorige artikel heb je geleerd hoe je Plausible kunt instellen, een privacyvriendelijke en betaalbare analyseoplossing. In dit artikel leer je hoe je Plausible kunt gebruiken om het leesgedrag van je bezoekers te verzamelen en bovenaan elk van je artikelen te tonen hoeveel mensen het hebben gelezen.

Informatie verzamelen over je bezoekers is geweldig, maar meer inzicht krijgen in hoeveel van hen je artikelen daadwerkelijk lezen is nog beter. Om dit te doen, moeten we de voortgang die je bezoeker maakt in een aantal fasen definiëren. Deze fasen zullen we later omzetten in doelen, die je gedetailleerde informatie geven over hoe goed je artikelen presteren.

Onze leesvoortgangsstappen gedefinieerd als doelen in Plausible
Hier zie je hoeveel mensen mijn eerste artikel hebben geopend, bekeken of gelezen

Leesgedrag volgen

We gebruiken een door Medium.com geïnspireerde "leestijd". Medium.com berekent de leestijd op basis van de gemiddelde leessnelheid van een volwassene (wat ongeveer 265 woorden per minuut is) en deelt het totale aantal woorden door dit getal. Dus als je artikel 2650 woorden bevat, duurt het ongeveer 10 minuten voor je bezoekers om het te lezen. Bij het meten van de voortgang definiëren we een aantal stappen om de voortgang aan te geven, elk vertegenwoordigt een percentage van de totale leestijd:

  • opened (0%): nog niet begonnen met lezen
  • peeked (10%): snel door het artikel gescrold
  • quarter-read (25%): begonnen met lezen, maar gestopt bij een kwart
  • half-read (50%): halverwege gelezen
  • three-quarter-read (75%): tussen halverwege of het hele artikel gelezen voor snelle lezers
  • read (100%): je artikel met grote aandacht gelezen

Het is ook belangrijk om een aantal scenario's in gedachten te houden.

Scenario 1 => de lezer wordt afgeleid en nu staat je artikel op de achtergrond

Dit is gemakkelijk te verzachten, we kunnen een event listener gebruiken om wijzigingen in de zichtbaarheid van documenten te detecteren en de timer te stoppen: document.addEventListener("visibilitychange".

Scenario 2 => de lezer loopt weg van de computer

Dit is iets moeilijker te detecteren, maar een optie is om een andere indicator van 'lezen' bij te houden, namelijk het scrollen. Dus naast de tijdsvoortgang volgen we ook de scrollvoortgang, en pas wanneer zowel de tijd als het scrollen een bepaald niveau bereiken, werken we de leesvoortgang bij naar de volgende status.

Klik op de knop om te zien hoe je het doet bij het lezen van deze pagina

Om de leesvoortgang van je bezoekers te volgen, heb ik een handige composable geschreven die je hier kunt bekijken: useReadProgress.ts

Naarmate het lezen vordert en de status verandert, werken we Plausible bij met aangepaste events. Hiervoor heb ik een andere composable geschreven, die je hier kunt bekijken: useReadProgressTracking.ts. Het zet de percentages van useReadProgress om in de gedefinieerde volgdoelen en stuurt tracking events naar Plausible.

Maak doelen aan voor elke stap in Plausible

Wanneer je de bovenstaande stappen als doelen in Plausible definieert, kun je een mooie trechter zien voor elk van je artikelen en hoe ver je bezoekers ermee komen.

Onze leesvoortgangsstappen gedefinieerd als doelen in Plausible
Onze leesvoortgangsstappen gedefinieerd als doelen in Plausible

Om doelen in Plausible in te stellen, ga je naar Site Settings > Goals en voeg je een doel toe voor elk van de stappen (opened, peeked, etc).

Je paginastatistieken lezen uit Plausible, met behulp van een Azure Function

Plausible biedt een Stats API die je kunt bevragen om bijvoorbeeld de statistieken van je aangemaakte doelen op te halen. Zie het volgende codevoorbeeld, dat laat zien hoe je de API kunt bevragen.

public async Task<PageReads> GetPageReads(string url)
{
    using var scope = _logger.BeginScope(new Dictionary<string, object> { { "url", url } });

    var uri = new Uri(url);
    var domain = uri.Host;
    var relativeUrl = uri.PathAndQuery;

    var payload = new
    {
        site_id = domain,
        metrics = new[] { "visitors" }, // Haal het unieke aantal leesevents op
        date_range = "all",
        filters = new[]{
            new List<object> { "contains", "event:page", new[] { relativeUrl } },
            new List<object> { "is", "event:goal", new[] {"read", "three-quarter-read", "half-read", "quarter-read", "peeked", "opened"} },
        },
        dimensions = new[] { "event:goal" },
    };

    var jsonPayload = JsonSerializer.Serialize(payload);
    _logger.LogInformation("Payload: {jsonPayload}", jsonPayload);

    var response = await SendRequest(jsonPayload);
    var responseContent = await response.Content.ReadAsStringAsync();
    _logger.LogInformation("Response: {responseContent}", responseContent);

    var queryResult = JsonSerializer.Deserialize<QueryResult>(responseContent);
    if (queryResult == null)
    {
        throw new InvalidOperationException("Failed to deserialize the response content.");
    }

    var resultsDict = queryResult.Results.ToDictionary(x => x.Dimensions.First(), x => x.Metrics.First());

    return new PageReads
    {
        Read = resultsDict.GetValueOrDefault("read"),
        ThreeQuarterRead = resultsDict.GetValueOrDefault("three-quarter-read"),
        HalfRead = resultsDict.GetValueOrDefault("half-read"),
        QuarterRead = resultsDict.GetValueOrDefault("quarter-read"),
        Peeked = resultsDict.GetValueOrDefault("peeked"),
        Opened = resultsDict.GetValueOrDefault("opened"),
    };
}
{
  "site_id": "{{site_id}}",
  "metrics": ["visitors"],
  "date_range": "all",
  "filters": [
    ["contains", "event:page", ["{{relative_url}}"]],
    [
      "is",
      "event:goal",
      [
        "read",
        "three-quarter-read",
        "half-read",
        "quarter-read",
        "peeked",
        "opened"
      ]
    ]
  ],
  "dimensions": ["event:goal"]
}
{
  "results": [
    { "metrics": [68], "dimensions": ["opened"] },
    { "metrics": [45], "dimensions": ["peeked"] },
    { "metrics": [40], "dimensions": ["quarter-read"] },
    { "metrics": [31], "dimensions": ["half-read"] },
    { "metrics": [21], "dimensions": ["three-quarter-read"] },
    { "metrics": [14], "dimensions": ["read"] }
  ],
  "meta": {},
  "query": {
    "site_id": "{{site_id}}",
    "metrics": ["visitors"],
    "date_range": ["2025-01-20T00:00:00+01:00", "2025-08-02T23:59:59+02:00"],
    "filters": [
      ["contains", "event:page", ["{{relative_url}}"]],
      [
        "is",
        "event:goal",
        [
          "read",
          "three-quarter-read",
          "half-read",
          "quarter-read",
          "peeked",
          "opened"
        ]
      ]
    ],
    "dimensions": ["event:goal"],
    "order_by": [["visitors", "desc"]],
    "include": {},
    "pagination": { "offset": 0, "limit": 10000 }
  }
}

Zie de volledige implementatie hier PlausibleService.cs en het hele project hier Bach.Software.API

Conclusie

Begrijpen hoe diep je publiek zich met je content bezighoudt is veel betekenisvoller dan alleen paginaweergaven tellen. Met slechts een paar regels code en de kracht van Plausible kun je nu nauwkeurig bijhouden of je lezers scannen of echt duiken in je content. Door scrollgedrag en leestijd te combineren, kun je je redactionele strategie verfijnen en identificeren wat het meest resoneert. En omdat het cookieloos is, sluit het aan bij moderne privacystandaarden zonder inzicht op te offeren.

Of je nu een solo-ontwikkelaar bent, een blogger, of een groter contentplatform beheert, deze aanpak geeft je diepgaande inzichten zonder de last van invasieve tracking. Probeer het uit op je volgende post, je zou verbaasd kunnen zijn over wat je lezers echt doen.

Over Jeroen Bach

Ik ben een Software Engineer en Team Lead met meer dan 15 jaar professionele ervaring. Ik ben gepassioneerd over het oplossen van complexe problemen door middel van eenvoudige, elegante oplossingen. Deze blog is waar ik technieken en inzichten deel voor het bouwen van geweldige software, geïnspireerd door echte projecten.

Jeroen Bach

Ontworpen in Figma en gebouwd met Vue.js, Nuxt.js en Tailwind CSS. Gedeployed via Azure Static Web App en Azure Functions. Website analytics worden aangedreven door Plausible Analytics, gedeployed met Azure Kubernetes Service.