Self-hosted service
toptal/keycodes avatar
toptal/keycodes

Keycodes: the Next.js site that answers what keyCode a key press returns

Easy visualizer for JavaScript KeyCodes

2,157 stars356 forksTypeScriptMIT

At a glance

What is it?
A small Toptal project behind keycode.info where you press a key and read the KeyboardEvent values. The interesting part is the migration the tree hints at and the manifest has not caught up with.
Who is it for?
Keycodes is small in scope and unusually thorough in the surrounding infrastructure. For a page that mostly listens for a keydown event and prints a few fields, the repository carries Docker and Compose files, Sentry on both client and server, ESLint and Stylelint with separate CI variants, Jest for units, Playwright for end to end, Happo for visual snapshots, and TypeScript type checking as its own script.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 6 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 11, 2026, and from our analysis. They are not legal advice.

Editorial analysis

Press a key, read the KeyboardEvent

The README describes the site in one sentence: a simple website where users press any key and instantly get the JavaScript Key or Key Code from the KeyboardEvent. GitHub's description for the repository says the same thing more tersely, as an easy visualizer for JavaScript KeyCodes, and the live site is keycode.info.

That is the entire product. The value is not the code itself, which anyone can find in documentation, but the immediacy of pressing the key and seeing what the browser reports on your own keyboard layout. The page links to a Tool and Event List for the fuller picture, which is the part that turns a lookup toy into a reference.

The README also says it is built with Next.js and TypeScript, which is true and undersells the point. What is interesting about this repository is not the complexity of the application, which is small, but the density of the tooling around it. The tree carries a Dockerfile, a Dockerfile.deploy, a docker-compose.yml, three Sentry config files, ESLint and Stylelint configs with separate ignore files, a Prettier config, a Playwright config, a Happo config, a nyc config, a husky directory with a lintstaged config, an nvmrc pinning a Node version, and a swcrc alongside a babel_.config.js.

The homepage is a Toptal open source project, and the design system is Toptal's own: the manifest lists @toptal/picasso and @toptal/picasso-provider as dependencies, along with @toptal/utilities-ui-library.

The manifest says Next 12, the tree looks like something newer

Here is a discrepancy worth naming, because it is the kind of thing that sends a new contributor down the wrong path.

The package manifest pins next at ^12.2.4. Meanwhile the repository tree contains both an app/ directory and a pages/ directory, plus a middleware.ts file and an .swcrc config. The App Router convention and the middleware file belong to Next 13 and later, while pages/ is the older router. Having both directories at once is what a migration in progress looks like, and the presence of babel_.config.js with a trailing underscore is a tell of its own: the build scripts rename that file into place when a Babel build is needed.

Those two facts cannot both be describing the same running application. Either the manifest is behind the source, in which case a fresh clone will not build as written, or the app/ directory is staged for a later cut and the live site still runs on the pages router.

The concrete thing to check is the pages/ directory against app/ for the current entry point, and the actual installed Next version in yarn.lock rather than the range in package.json. Read the framework version from the lockfile and the folder that has the routes, and you will know which router this repository is actually on.

It is worth remembering this pattern generally. In a small project the manifest is written by hand and drifts; in a large one CI keeps it honest. Here the answer is in the tree.

Four ways to run it, including two containers

The README's Development section asks for node and yarn and gives the short version as three steps: install dependencies with yarn, run the development server with yarn dev, and open localhost:3000. It also mentions that you can start the project with docker-compose up.

The Dockerfile is where this project shows its age and its care at the same time:

bash
FROM node:16-slim as base

ENV PORT 80

ARG user
RUN mkdir /app && chown -R $user:$user /app
USER $user
WORKDIR /app

COPY --chown=$user:$user package.json yarn.lock /app/
RUN yarn install

COPY --chown=$user:$user . /app
RUN yarn build

CMD yarn start -p $PORT

Two details are worth noticing. The ENV line uses the legacy space-separated form, `ENV PORT 80`, rather than the modern `ENV PORT=80`, which current Docker warns about. And the ARG is declared without a default, so building the image without passing a user produces a container with an empty USER, which fails. The Compose file fills that gap:

bash
version: '3'
services:
  app:
    build:
      context: .
      args:
        user: ${RUN_AS_USER:-node}
    command: bash -c "yarn install && yarn dev"

So Compose defaults the build arg to node and overrides the command to install and run the dev server with the source mounted as a volume on port 3000, while the Dockerfile alone is built for production. That pairing is why the README lists docker-compose up as an alternative to the local three steps.

Node 16 in the base image is old by current standards, and it lines up with an .nvmrc at the root for the local setup. If you are copying this Dockerfile, both of those are the first things to update.

Testing, linting and logging have their own documents

The README keeps its tooling documentation in a docs/ directory with three pages: Testing, Linting and Logging. That is unusual and useful, because these are the parts of a Next.js project most likely to need explaining.

The scripts tell the story. Unit tests run through Jest with a config at config/jest.config.js, and there are CI variants that add ci, silent and a worker cap, plus a coverage variant that collects coverage. End to end tests run through Playwright, with a build step that has to rename the Babel config into place first, a start script that runs the production server, and a wait script that blocks until localhost:3000 answers with a three minute timeout. That wait script is the small piece of infrastructure that makes the difference between a flaky end to end suite and a reliable one.

Happo is configured at the root for visual regression, and there is a nyc report script alongside the Jest coverage, which suggests coverage tooling carried over from an earlier setup.

Linting is layered. ESLint covers js, jsx, ts and tsx with a fix variant and a quiet CI variant. Stylelint handles scss and css with its own fix and CI scripts. Prettier formats, lintstaged runs on commit, and husky installs through the prepare script. There is also a types:check script running tsc with noEmit, which keeps type errors out of the lint path rather than mixing the two.

Error reporting is Sentry, configured separately for client and server with a sentry.properties file, which is the arrangement that lets server-side errors report without shipping server config to the browser.

Contributing, and the license stated twice

The Contributing section of the README is generous in a way that is worth reading as a signal about the project. It invites bug reports, feature requests, code review and documentation fixes, and asks contributors to read CONTRIBUTING.md at the repository root, which covers submitting pull requests, coding guidelines, how to find issues, and how to use the labels.

The code of conduct is the Contributor Covenant version 2.1, linked explicitly. And the license appears twice: a Contributor License Agreement heading that says licensed under the MIT license with a link to the LICENSE file in the repository, and an actual LICENSE file at the root of the tree. GitHub reports MIT as well, so all three agree, which is worth noting precisely because the sibling projects in this set did not all agree with themselves.

The maintainer language is a little unusual, with a paragraph asking what the keycodes community thinks and thanking people for taking the time to contribute. For a site this small, that framing fits: the project is a public utility and the contributions that arrive are usually single-key edge cases someone hit on their own keyboard.

One detail that hints at how the site handles its data: @sindresorhus/slugify is a dependency. A slugify utility in a key code site implies the site mints shareable or linkable URLs per key or event, so a result you are looking at can be sent to someone else rather than retyped by hand. That is the kind of small feature that turns a personal reference into something people actually link to.

How to start reading it, and what to check first

Start with the key press handler. In a project this size, the application logic is a keydown listener and a rendering of the event's fields, so lib/ and components/ in the tree are where the substance is, and pages/ or app/ is only the mounting point. Reading the handler tells you exactly which KeyboardEvent properties the site surfaces, which is the answer to any question about what it shows.

Next, check the framework version question described above, because it determines which router's conventions the rest of the code follows. The manifest's range and the tree disagree, and the lockfile settles it.

Then read the tool and event list linked from the README, since that is the part of the site that carries the reference weight rather than the novelty weight.

GitHub reports 2,157 stars, 357 forks and 28 open issues on the MIT licensed repository, which is not archived and was last pushed on 2026-09-13. There are no published releases, so there is no version history to read and no changelog; the commit log is the history. The repository also has middleware.ts at the root, an .env.local.example for local configuration, config/ for build and test configuration, styles/ for the stylesheets Stylelint checks, public/ for static assets, and test/ for the test suites.

Editorial conclusion

Keycodes is small in scope and unusually thorough in the surrounding infrastructure. For a page that mostly listens for a keydown event and prints a few fields, the repository carries Docker and Compose files, Sentry on both client and server, ESLint and Stylelint with separate CI variants, Jest for units, Playwright for end to end, Happo for visual snapshots, and TypeScript type checking as its own script. What the tree also hints at is a framework migration in progress: the manifest still pins Next 12 while the repository holds an app/ directory, a middleware file and an SWC config, which is a combination that belongs to a newer major version. If you are reading this to reuse the component, the page itself is small; if you are reading it to judge the setup, the package manifest is the file most likely to be behind the source tree, and the presence of both a pages/ directory and an app/ directory is where you would start checking. Press a key and read the event fields is the whole idea, and the tool and event list is where the depth is.

Frequently asked questions

How do I run the Keycodes site locally?

The README asks for node and yarn, then gives three steps: run yarn to install dependencies, run yarn dev to start the development server, and open localhost:3000. There is also a docker-compose up alternative, and the Compose file builds the image, mounts the source as a volume and runs the same install and dev commands inside the container.

What is Keycodes and who maintains it?

Keycodes is the website at keycode.info, where you press any key and instantly get the JavaScript key and key code from the KeyboardEvent. GitHub reports it as an easy visualizer for JavaScript KeyCodes, and it is a Toptal open source project built with Next.js and TypeScript using Toptal's Picasso design system.

Which Next.js version does the Keycodes repository use?

The package manifest pins next at 12.2.4, but the tree contains both an app/ directory and a pages/ directory along with a middleware.ts file and an .swcrc config, which belong to a newer major version. Check yarn.lock for the installed version and look at which directory holds the routes to see which router the code is actually on.

How does the Keycodes project test its code?

Unit tests run through Jest with a config at config/jest.config.js, and end to end tests run through Playwright against a production build. A wait script polls localhost:3000 with a three minute timeout before the suite runs, and Happo is configured for visual regression. There are separate CI script variants for lint, styles and coverage.

Official sources

  1. Issues
  2. License: MIT
  3. Project website
  4. README
  5. toptal/keycodes on GitHub
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.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/toptal-keycodes.svg)](https://hysenlabs.com/projects/toptal-keycodes)