Shared ESLint config for Micro:bit Educational Foundation TypeScript projects. Flat config, ESLint 10, with type-aware linting via typescript-eslint's project service.
Two entry points:
@microbit/eslint-config— base config for TypeScript projects:eslint:recommended, typescript-eslintrecommended-type-checked, and our shared rule adjustments, witheslint-config-prettierlast so formatting is left to Prettier.@microbit/eslint-config/react— the base config plus eslint-react (recommended-type-checked) andeslint-plugin-react-hooks.
Both apply only to TypeScript files (.ts, .tsx, .mts, .cts), and
both ignore generated output common to our repos (dist, build,
coverage, Playwright reports, Storybook builds, Panda's styled-system
output and panda.config.ts) plus .storybook and .claude directories.
We use eslint-react rather than eslint-plugin-react, which has had no
release since April 2025 and crashes on ESLint 10. eslint-react covers
the same ground with more rules and no compatibility workarounds.
Both eslint-react and eslint-plugin-react-hooks implement the hooks and
React Compiler rules. We keep the React team's versions and switch off
eslint-react's equivalents, so a given problem is reported once.
naming-convention-* (ref, context and id naming) is off. It is house
style rather than correctness, and we have no shared position on it.
Rules that depend on the React version follow the React resolved from the linted project. Libraries supporting a range of React versions should pin the setting to the oldest they support, or they will be told to adopt APIs their consumers may not have:
{
settings: {
"react-x": {
version: "17.0.0",
},
},
}The react config switches react-hooks/rules-of-hooks off in two places
where it misfires on established idioms: under e2e directories, where
Playwright fixtures take a callback named use that the hooks plugin
treats as the React 19 use hook, and in *.stories.{ts,tsx} files,
whose render functions are components in practice but not
component-cased.
Install alongside ESLint:
npm i -D eslint @microbit/eslint-configCreate eslint.config.js:
import microbit from "@microbit/eslint-config/react";
export default [
{
// Project-specific ignores, e.g. build scripts kept in plain JS.
ignores: ["bin", "deployment.cjs"],
},
...microbit,
{
// Project-specific rule overrides, ideally temporary ones on the way
// to the shared config. Keep divergence visible here rather than
// adding it to the shared config.
rules: {},
},
];Suggested lint script:
"lint": "eslint . --max-warnings 0"Unused eslint-disable directives are reported as errors by the shared
config, so the old --report-unused-disable-directives flag is not needed.
Type-aware rules resolve types using the nearest tsconfig.json to each
linted file (typescript-eslint's projectService). Files that no
tsconfig.json includes fail with a parse error; either extend the
tsconfig, add an ignore, or allow specific files:
{
languageOptions: {
parserOptions: {
projectService: {
allowDefaultProject: ["*.config.ts"],
},
},
},
}npm run ci runs lint, format check, and tests. The tests run ESLint
programmatically against fixtures in test/fixtures.
To try an unreleased version in a consuming project, use a file dependency from a sibling checkout:
npm i -D ../eslint-configor npm link if you prefer.
Create a GitHub release; CI publishes to npm with the version derived from the tag.