Widgets and badges
Show a counter anywhere without writing code: a widget for web pages, an SVG badge for READMEs, and an invisible pixel for emails. All three work with public counters and need no token.
The widget
Add an element with the class countjar wherever the number should appear, and load the script once per page:
<div class="countjar" data-workspace="acme" data-counter="visits"></div>
<script src="https://countjar.vercel.app/embed.js" async></script>
Each page load counts one visit and shows the total, animating up from zero. The widget draws inside its own Shadow DOM, so your page's styles don't leak into it and its styles don't leak out. It doesn't set cookies. Like, poll and rating widgets remember a visitor's choice in their browser's local storage, so the button shows what they picked last time.
The Embed tab on each counter in the dashboard builds this snippet for you and shows a live preview as you change the options.
Widget options
Set options as data- attributes on the element. Options without a value, like data-readonly, are switched on just by being there.
| Parameter | Description |
|---|---|
data-workspace | Required. The workspace address. |
data-counter | Required. The counter name. |
data-type | count (default), like, poll or rating. Match the counter's type. |
data-theme | light (default), dark, or glass for a frosted look over images and colour. |
data-icon | eye (default), heart, star, fire, download, users, thumbs or bolt. |
data-label | Text after the number, such as views. |
data-readonly | Show the number without counting a visit. |
data-window | Show recent activity instead of the all-time total, such as 24h or 7d. |
data-abbreviate | Shorten big numbers: 12.4k, 3.1M. |
data-start | Add this to the displayed number, for example to include visits from before you used CountJar. |
data-hide-zero | Hide the widget until the number is above zero. |
data-no-anim | Show the number straight away instead of counting up. The widget also skips the animation for visitors who've asked for reduced motion. |
data-no-link | Remove the small CountJar link. |
data-color, data-bg, data-accent | Any CSS colour for the text, background and icon or button highlight. |
data-question | Poll widgets only. The question above the buttons. Defaults to “Was this helpful?”. |
<div class="countjar" data-workspace="acme" data-counter="downloads"
data-icon="download" data-label="downloads" data-theme="dark" data-abbreviate></div>
Like button
Create a like counter, then:
<div class="countjar" data-type="like" data-workspace="acme" data-counter="post-42-likes"></div>
Visitors click the heart to like. Each visitor can like once: the server only counts their first like, and the button stays filled in on their next visit. Showing the button doesn't count anything.
Poll
Create a poll counter, then:
<div class="countjar" data-type="poll" data-workspace="acme" data-counter="docs-helpful"
data-question="Did this page answer your question?"></div>
Visitors pick yes or no and then see the results as percentages. Add data-readonly to only show the results.
Star rating
Create a rating counter, then:
<div class="countjar" data-type="rating" data-workspace="acme" data-counter="recipe-rating"></div>
Shows five stars with the current average and the number of ratings. Each visitor can rate once.
Single-page apps
The script finds widgets when the page loads. If your app adds them later, for example after a route change in React or Vue, ask it to look again:
window.CountJar?.scan();
Widgets that are already showing are left alone, so it's safe to call as often as you like. You can pass an element to only look inside it: CountJar.scan(document.querySelector("#comments")).
Badges
A badge is an SVG image that shows a counter's value. It suits places that allow images but not scripts, such as a GitHub README:

Or in HTML:
<img src="https://countjar.vercel.app/badge/acme/repo-views.svg?style=social&label=visitors" alt="Visitor count">
Every time the image loads it counts a view, unless you add readonly=1. Badges are sent with headers that tell GitHub's image proxy not to cache them, so the number stays current.
For like counters the badge shows the number of likes, polls show the percentage who said yes, and ratings show the average stars.
Badge options
| Parameter | Description |
|---|---|
style | flat (default), social (like GitHub's buttons) or big (a larger number, for websites). |
label | Text on the left. Defaults to views, or likes for like counters. Up to 40 characters. |
color | Colour of the value side: amber (default), green, blue, red, violet, pink, slate, black, or a hex code without the #, like 0ea5e9. |
labelColor | Colour of the label side, in the same formats. |
abbreviate | 1 to shorten big numbers: 12.4k, 3.1M. |
readonly | 1 to show the value without counting a view. |

Tracking pixel
A 1×1 transparent GIF that counts one view each time it loads. Use it in emails, or anywhere you can add an image but don't want anything visible:
<img src="https://countjar.vercel.app/p/acme/newsletter-opens.gif?issue=42" width="1" height="1" alt="">
Extra parameters like issue are saved with the event as custom properties, so one counter can track every issue and you can filter by issue later.
Public counters only
Widgets, badges and pixels never use a token, so they only work with public counters. A private or missing counter shows a dash in the widget and “not found” on a badge, and isn't counted.
Counting is rate limited per visitor, to stop one person running the number up. Over the limit, badges and pixels still load but the visit isn't counted.