jwt.io is built to explain tokens and to show you why yours fails
Developer-centric site to create awareness of JWTs and troubleshoot them.
At a glance
- What is it?
- The jsonwebtoken.github.io repository holds the site that most developers meet when a JWT does not verify, and its stated purpose is developer-centric awareness and troubleshooting rather than documentation. It is a Next.js application with MDX content, a live token debugger, and a generated page that ranks the JWT libraries available in each language by popularity.
- Who is it for?
- Adopt this repository if you want to run a copy of jwt.io for internal documentation, since it is a complete Next.js application with MDX content, a token decoder and a test suite. Skip it if you are looking for a JWT library, because this is the documentation site rather than an implementation, and if you cannot accept a site whose library rankings depend on a scheduled GitHub token.
- Can I use it commercially?
- Check first. The repository uses a licence we do not classify automatically, so read its LICENSE file before any commercial use.
- Is it still maintained?
- Yes. The repository last received commits 7 days ago.
- What is it written in?
- Mainly TypeScript, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 22, 2026, and from our analysis. They are not legal advice.
Editorial analysis
The site exists for troubleshooting, not for reference
The repository description is short and it sets the tone for everything else. This is a developer-centric site to create awareness of JWTs and troubleshoot them.
That word troubleshoot is the important one. A documentation site explains how a thing works in the order the author found natural, and a troubleshooting site answers the question someone actually arrives with, which is usually why this token is not verifying.
The homepage is jwt.io, and the repository is named for the site rather than the other way round, which tells you the site is the artefact and the repository is where it is built. The default branch is master, the last push was on 2026-10-16, and the release tags are named extension-2.1.1, extension-2.1.2 and extension-2.1.3, all from 2018, which tells you the extension part of the project has been stable for a long while while the site itself has kept moving.
The stack is Next.js on the app router, with the package file naming the project next-jwt at version 0.1.0 and marking it private, meaning the package is never published. The type field is module, so the source is ES modules throughout.
The licence is listed as Other, a custom licence GitHub cannot classify, with the terms in the LICENSE file, and there is a separate note in the README that the project is licensed under the Apache License 2.0. Those two statements sit in different places and are worth reconciling if you intend to reuse anything.
The MDX content pipeline is the interesting architectural choice
The dependency list is where you can see what kind of site this is, and it is not a blog.
The content is MDX, with the @next/mdx package, the @mdx-js loader and @mdx-js/react, and there is a mdx.d.ts file at the root providing the types. MDX is Markdown that can import and render React components, which is what lets the documentation show a working token decoder inline in a sentence rather than describing one.
The token work itself is done by jose at 6.2.5, which is the modern JavaScript JWT library, alongside node-forge and base64url for the cryptographic and encoding sides, and zod at 3.23.8 with schema-dts for the JSON Schema definitions that describe a token structure. The zod pairing with schema-dts is how the site can present a typed schema for a token and validate a decoded one against it.
Prismjs does the syntax highlighting, and rehype-raw lets the markdown pipeline pass through raw HTML, which is a common need in documentation that shows example output.
The interface pieces are react-select for the dropdowns a token debugger needs, react-aria-components for accessible behaviour, and react-markdown for rendering markdown at runtime, with clsx for class composition and zustand for state. There is also @formatjs/intl-localematcher, which handles locale negotiation, and bowser and negotiator for browser and version detection.
So the dependencies describe a documentation site with an interactive tool, an internationalisation layer and a schema-driven validation story. That is a fairly specific set of requirements, and it is consistent with a site whose purpose is to let someone paste in a token and find out what is wrong with it.
The library data is generated, not written by hand
There is one content page that is generated rather than authored, and the mechanism is documented in detail because it needs a credential.
The page in question is the libraries page, and the data behind it comes from a script. The script is generate:library-data, which the package file shows as `tsx src/scripts/generate-library-data.ts`, and the output is `src/data/libraries-next.json`.
There are two ways to run it. If the GitHub CLI is already authenticated, you can update the library star counts using your existing credentials:
GITHUB_TOKEN="$(gh auth token)" npm run generate:library-dataAlternatively, you generate a .env file based on the .env.example file:
cp .env.example .envAnd then add your personal GitHub access token to the .env file:
GITHUB_TOKEN=Then run the generator:
npm run generate:library-dataBoth commands update the same output file.
The reason for the token is that the library rankings are based on GitHub stars, and fetching those needs the GitHub API. The .env.example file in the repository contains exactly one line, the GITHUB_TOKEN entry, which is a good sign about how little configuration the project needs.
The production behaviour is worth noting. Production deployments refresh the star counts automatically using the workflow's built-in GitHub token, and the production workflow also runs every Monday at 06:00 UTC.
So there are two update paths. A scheduled workflow keeps the live site current without anyone doing anything, and the local script is for when you want to regenerate the data as part of your own workflow or test it before pushing.
Three test layers, and the dev server runs on a non-standard port
The testing setup has three distinct layers, which is more than a site of this kind usually has.
Unit tests run under vitest, with the test script being `vitest` and a coverage script that runs `vitest run --coverage` using the istanbul coverage provider. Then there are end-to-end tests, with an e2e/ directory, a playwright.config.ts and a separate script for the Playwright UI mode that sets NODE_OPTIONS to disable experimental type stripping. And there is a tests/ directory alongside the e2e directory, which suggests a further layer between the two.
End-to-end tests on a site like this make sense, because the core interaction is typing a token into a box and seeing a decoded result. That is exactly the kind of flow that unit tests cannot cover and that a user would immediately notice if it broke.
The type checking is separate too, with a typecheck script running `tsc --noEmit`, so a type error fails without running the test suite at all.
One detail is worth noticing for anyone who runs this locally. The dev script is `next dev -p 1234 --webpack`, so the development server runs on port 1234 rather than the Next.js default of 3000, and it explicitly passes the webpack flag even though the build script does the same.
The webpack flag is a deliberate choice in a project on Next 16, where Turbopack is the default bundler. Both the dev and build scripts opt out of it, which means the site builds with webpack rather than with the newer bundler. For a site that is mostly static content with a few interactive islands, that is a conservative choice, and it also means the local environment and the deployed environment behave the same way, which is worth more than build speed for a documentation site.
There is also a vercel.json at the root, which is where the deployment configuration for Vercel lives.
A site with a design system rather than a page of text
The visual side of the repository is more substantial than a documentation site usually is, and the file list explains why.
There is a public/ directory for static assets, a styles/ area reached through the sass dependency, and a types/ directory with the shared TypeScript types. The components live under src/ and there is a separate src/data directory holding the generated library data.
The Sass dependency at 1.74.1 with the PostCSS stack is a styling choice worth noting against the rest of the dependency list, which otherwise leans on utility-class conventions with clsx and tailwind-merge. Sass here is for the site's own component styles rather than for a utility framework, and the postcss configuration sits alongside a next.config.mjs.
The analytics dependency is @vercel/analytics, which is the natural pairing with a Vercel deployment, and the deployment instruction in the README is to use the Vercel Platform from the creators of Next.js.
The overall picture is of a site that takes its own design seriously. The font handling uses next/font to automatically optimize and load Inter, described as a custom Google Font, and the font optimization is handled by the framework rather than by a link tag.
That combination, an interactive token tool, a schema-validated decode result, a library ranking page fed by the GitHub API, MDX content with embedded components and a scheduled refresh, is more than a documentation site. It is an attempt to make the format legible to a developer who has never seen one, which is a harder design problem than publishing a reference.
Where this sits against the libraries it documents
The useful way to think about this project is as a piece of developer infrastructure rather than as a page.
A JWT is a format with a small number of moving parts, and the failure modes are well known: a wrong secret, a clock skew, an algorithm mismatch where a server expects RS256 and the token says HS256, an audience that does not match, or a base64url encoding with a padding character left in. Each of those produces an error message that names the failure without explaining why it happened.
A tool that takes the token, shows you the header and payload, decodes the signature, and tells you which of those is wrong is doing something a language-agnostic reference cannot. It works for the library you are not using, and for the language you are not writing in, which is the situation most people hit when a token from another team does not verify.
Against the official library documentation, the difference is audience. Library docs explain the API of a specific implementation. This site explains the format, and points at implementations for each language.
Against the blog posts and Stack Overflow answers that fill the same role, the difference is that it is maintained by the people behind the token format, so it does not go out of date silently, and it is one place rather than a hundred.
The libraries page is the piece that ties the two together. It ranks the JWT libraries available in each language by popularity, which is a genuinely useful piece of information, since the choice of library is the first decision and the answer differs by ecosystem. Generating that from the GitHub API rather than maintaining it by hand means it stays current, and the weekly refresh on Monday at 06:00 UTC keeps it moving.
The one limitation of the whole approach is the same as any tool of this kind. It can tell you that a token is malformed and it can decode one, but verifying a signature against a key is something the tool deliberately does not do for you, since that would mean shipping keys to a website.
Editorial conclusion
Adopt this repository if you want to run a copy of jwt.io for internal documentation, since it is a complete Next.js application with MDX content, a token decoder and a test suite. Skip it if you are looking for a JWT library, because this is the documentation site rather than an implementation, and if you cannot accept a site whose library rankings depend on a scheduled GitHub token. Verify first which Node and package manager you use, that you know how the library data is generated before you edit the content, since the star counts come from a script rather than from a hand-maintained file, and that the content is MDX rather than Markdown, since that changes how you add pages.
Frequently asked questions
What is the jsonwebtoken.github.io repository for?
It holds the developer-centric site at jwt.io, created to build awareness of JWTs and to help people troubleshoot them. It is a Next.js application built with create-next-app, with MDX content, a jose-based token decoder, zod and schema-dts for JSON Schema definitions, and a libraries page that ranks JWT implementations per language by GitHub star count.
How do I regenerate the library star count data?
Run the generator script, which is `tsx src/scripts/generate-library-data.ts` behind the npm script `generate:library-data`. It needs a GitHub token, either passed inline with `GITHUB_TOKEN="$(gh auth token)" npm run generate:library-data` or placed in a .env file copied from .env.example. Both commands update src/data/libraries-next.json, and in production the counts refresh automatically with the workflow's built-in token.
How do I run and build the jwt.io site locally?
Run `npm run dev` and open http://localhost:3000. The dev script is `next dev -p 1234 --webpack`, so it serves on port 1234 and opts out of Turbopack by passing the webpack flag, and the build script does the same. Editing app/page.tsx updates the page automatically, and the project uses next/font to optimize and load Inter.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/jsonwebtoken-jsonwebtoken-github-io)Was this useful?