HTMLHint: a static analyser for HTML markup
⚙️ The static code analysis tool you need for your HTML
At a glance
- What is it?
- HTMLHint lints the HTML you actually ship, catching unclosed tags, duplicate ids and inline style attributes before they reach a browser. It is a Node.js tool with an npm install, a .htmlhintrc config file and a programmatic API.
- Who is it for?
- Adopt HTMLHint if you maintain hand-written HTML, email templates or server-rendered pages and want markup errors caught at build time rather than in a browser. Skip it if your HTML is compiled from JSX or another template language, where it will only ever see generated output.
- 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 1 day ago.
- What is it written in?
- Mainly JavaScript, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 1, 2026, and from our analysis. They are not legal advice.
Editorial analysis
The problem HTMLHint solves
HTML is forgiving in a way that most languages are not. A browser will happily render a page with an unclosed div, a duplicated id, or a style attribute that should have been a class, and the page will look fine until something downstream depends on the structure being correct. Accessibility tooling, CSS selectors, DOM queries in tests and screen readers all read the same tree, and all of them break quietly when the markup is malformed.
HTMLHint is a static analyser that reads HTML as a document rather than as a string. It is aimed at people who write or generate HTML and want a machine to check it before a human does: template authors, email developers, teams maintaining server-rendered views, and anyone who wants markup checked in CI alongside a JavaScript linter. The project describes itself as "the static code analysis tool you need for your HTML," and the scope is deliberately narrow. It does not check CSS, it does not check JavaScript, and it does not attempt to validate against a DTD.
That narrowness is the point. A rule set that only concerns markup produces findings a reviewer can act on without sorting through unrelated warnings.
How HTMLHint parses and reports
The package exposes a verify function that takes an HTML string and returns a list of hints. Each hint carries information about a linting error, and the README's example logs that list directly. The CLI is a thin wrapper over the same function: it reads a file, a glob or a URL, runs verify, and formats the result.
The parser is configurable. The repository ships a parser-preset.js at the top level, and the source lives under src/ with a separate website/ directory for the documentation site. Rules are the unit of configuration, and the README shows three ways to select them: a config file discovered by walking up the directory tree, an explicit --config path, and a --rules flag that takes a comma-separated list. Inline comments in the HTML itself can also set rules for a single file.
Output formatting is not limited to the default. The package.json defines two scripts, htmlhint:html and htmlhint:sarif, which run the CLI with --format html and --format sarif respectively. SARIF matters if you want findings to appear in a code scanning interface rather than in a terminal log. The repository layout also includes a bin/ directory holding the htmlhint executable, and the published package ships only bin and dist, so the TypeScript source is not part of the npm tarball.
Installing HTMLHint and running a first check
HTMLHint requires Node.js 20 or later. The README documents two installation paths. For a project-local install, add it as a development dependency:
npm install htmlhint --save-devAfter that, the binary is available under node_modules. The README runs it against a single file and against a glob:
./node_modules/.bin/htmlhint www/index.html
./node_modules/.bin/htmlhint www/**/*.htmlIf you would rather have the command available everywhere, install it globally instead. The README notes this is the option for tools that run across all of your projects:
npm install htmlhint -gThe global install gives you a bare htmlhint command, and the README shows it can also analyse a live URL rather than a file on disk:
htmlhint https://htmlhint.com/If you are embedding the check in a build script rather than a shell, the programmatic API is the better entry point. The README gives an ESM form and a CommonJS form; the ESM one imports the named export and calls verify with the HTML content as a string:
import { HTMLHint } from 'htmlhint'
const htmlVerificationHints = HTMLHint.verify(localHtmlContent)
console.log('htmlVerificationHints', htmlVerificationHints)The return value is a list of hints, one per finding. The README does not document the shape of an individual hint object beyond saying it contains information on the linting error, so if you plan to consume the list programmatically you should inspect a real result before writing code against it.
Configuring rules with .htmlhintrc
Configuration is the part of HTMLHint that decides whether it is useful or annoying. With no arguments, the CLI searches for a .htmlhintrc file in the current directory and in every parent directory, then applies whatever it finds. Running the bare command or pointing it at a file both trigger that search:
htmlhint
htmlhint test.htmlIf your config lives somewhere else, or you want more than one profile, pass the path explicitly. The README uses htmlhint.conf as the example filename, which is worth noting because the file does not have to be named .htmlhintrc when you point at it directly:
htmlhint --config htmlhint.conf test.htmlFor one-off checks you can skip the file entirely and name the rules on the command line. The README's example enables tag-pair and sets id-class-value to the underline convention:
htmlhint --rules tag-pair,id-class-value=underline index.htmlThe equals sign is how a rule receives a value, so id-class-value=underline is a rule name plus a setting, not two separate rules. Rules can also be scoped to a single document with an inline comment, which the README places at the top of the file:
<!--htmlhint tag-pair,id-class-value:underline -->
<html>
<head>
...
</head>
</html>Note that the inline form uses a colon before the value while the CLI form uses an equals sign. That inconsistency is in the documentation as written, and it is the kind of detail worth confirming against the full rules page before you commit a config to a repository.
Where HTMLHint is the wrong tool
The most important limitation is structural: HTMLHint analyses HTML text. If your markup is produced by a template engine, a component framework or a build step, the linter sees either the template syntax or the compiled output, not the document a browser will receive. Running it over JSX or a templating language will produce findings about syntax the tool was not designed to parse, and running it over generated output means you are linting an artefact rather than the source that produced it.
The second limitation is that a passing run is not a correctness guarantee. A rule set can only catch what it has rules for. The README links to a rules page rather than listing them inline, so the exact coverage is not visible from the repository front page, and there is no claim of standards validation anywhere in the README. Treat a clean run as "no configured rule fired," not as "this markup is valid."
The third is configuration drift. Because the CLI walks up parent directories looking for .htmlhintrc, a file in a home directory or a monorepo root can silently apply to a project that never intended to inherit it. Teams that add a config at the top of a large repository should expect to check what the effective rule set actually is in each subdirectory. The README does not document a flag for printing the resolved configuration, so verifying that requires reading the config chain by hand or inspecting the source.
Finally, the repository's package.json declares version 2.0.0-beta-1 while the most recent published release listed is v1.9.2 from 2026-03-05. Anyone depending on the npm package should confirm which line they are installing rather than assuming the repository state matches the registry.
HTMLHint compared with a validator
The nearest alternative in kind is an HTML validator such as the W3C's Nu checker. The difference is what each one considers a defect. A validator checks a document against the HTML specification: element nesting, attribute names, required attributes, content models. HTMLHint checks a document against a configurable set of style and correctness rules, and you choose which ones apply. tag-pair and id-class-value are rules about conventions a team has agreed on, not rules the specification mandates.
That distinction decides which tool you want. If your goal is conformance, a validator is the right instrument and HTMLHint will not substitute for it. If your goal is consistency across a codebase, a validator will not help you enforce a naming convention for id and class values, and HTMLHint will. The two are complementary rather than competing, and a project with strict requirements could reasonably run both.
Within the JavaScript tooling ecosystem, HTMLHint occupies the same slot for HTML that a JavaScript linter occupies for scripts: a fast, configurable, exit-code-driven check that fits into a pre-commit hook or a CI step. The SARIF output format defined in package.json is the clearest signal of that intent, since SARIF exists to feed findings into code scanning dashboards rather than to be read by a person.
Licence, maintenance and upgrade cost
HTMLHint is released under the MIT licence, and the repository includes a LICENSE.md at the top level. MIT permits use, modification and redistribution provided the copyright notice and permission notice are preserved. That is permissive enough for commercial and closed-source use, but this is a description of the licence text, not legal advice, and organisations with licence review processes should run it through them.
The repository is not archived, and the last push was on 2026-09-23, so the codebase is receiving commits. Recent tagged releases are v1.9.0 on 2026-02-10, v1.9.1 on 2026-02-11 and v1.9.2 on 2026-03-05. The gap between the release cadence and the commit activity is worth noting if you depend on tagged versions: the npm package you install may lag the repository state.
Upgrade cost is driven by the rule set rather than the runtime. The package requires Node.js 20 or later, so a project on an older runtime cannot adopt it without upgrading Node first. The published tarball contains bin and dist only, which means the TypeScript types come from dist/core/core.d.ts and the compiled entry point is dist/core/core.js. Version 2.0.0-beta-1 in package.json indicates a major line in progress, and a major version is where rule defaults and the configuration surface are most likely to shift. Pinning the version in package.json and reading the changelog before a major bump is the cheap precaution here.
Editorial conclusion
Adopt HTMLHint if you maintain hand-written HTML, email templates or server-rendered pages and want markup errors caught at build time rather than in a browser. Skip it if your HTML is compiled from JSX or another template language, where it will only ever see generated output. Before rolling it out, run the CLI once over your real templates and read the reported rule names, because the default rule set is opinionated and the README does not document a rollback path for a rule that turns out to be noisy.
Frequently asked questions
How do I install HTMLHint?
Install it locally with npm install htmlhint --save-dev, or globally with npm install htmlhint -g if you want the command available across all your projects. HTMLHint requires Node.js 20 or later.
Is there an HTMLHint extension for VS Code?
The README does not document a VS Code extension. It covers the npm package, the command line interface and a programmatic API, and links to the project website for further documentation.
What file does HTMLHint read its configuration from?
By default it searches for a .htmlhintrc file in the current directory and in all parent directories. You can point it at a different file with the --config flag, for example htmlhint --config htmlhint.conf test.html.
Can HTMLHint check a URL instead of a local file?
Yes. The README shows the globally installed command being run against a live URL, for example htmlhint https://htmlhint.com/.
Can I use HTMLHint from JavaScript instead of the command line?
Yes. The README documents both an ESM import and a CommonJS require of the HTMLHint named export, then calling HTMLHint.verify with your HTML content to get back a list of hints.
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/htmlhint-htmlhint)