Presets
A preset is a config that your config merges in first. Use a preset to share one set of rules across sites, or to switch rules for one environment, such as a staging deploy.
Use a preset
Add presets to the presets field of seolint.config.ts:
import * as config from "seolint/config";
import * as presets from "seolint/presets";
export default config.define({
host: "https://example.com",
presets: [presets.strict],
rules: { "og.*": false },
});
To use a different preset for each environment, choose it in the config:
import * as config from "seolint/config";
import * as presets from "seolint/presets";
let staging = process.env.DEPLOY_ENV === "staging";
export default config.define({
host: staging ? "https://staging.example.com" : "https://example.com",
presets: staging ? [presets.staging] : [presets.strict],
});
How presets merge
seolint merges each preset in order, then your config. A preset can have its own presets, which merge in first.
| Field | Merge |
|---|---|
rules |
A later setting replaces an earlier one for the same rule. |
plugins |
Plugins are added together, in order. |
ignore |
Patterns are added together, in order. |
host |
The last one that is set wins. |
inputs |
The last one that is set wins. |
A rule pattern, such as "og.*", also replaces every earlier setting that it matches. So one pattern in your config undoes a preset for a group of rules:
export default config.define({
presets: [presets.strict], // sets "og.title": "error", "og.image": "error", ...
rules: { "og.*": "warn" }, // every og rule is a warning again
});
Inside one rules object, patterns apply first, then exact names. So { "og.*": false, "og.title": true } turns on only og.title.
Built-in presets
Import the built-in presets from seolint/presets:
strictturns every rule that defaults to a warning into an error.essentialskeeps only the rules that directly affect search results.stagingallowsnoindexon staging and preview deploys.
Write your own
A preset is a config. Make one with define(), the same as seolint.config.ts:
// seolint.base.ts
import * as config from "seolint/config";
import * as presets from "seolint/presets";
export default config.define({
presets: [presets.strict],
ignore: ["drafts/**"],
rules: {
"title.content.length": { max: 70 },
"twitter.*": false,
},
});
// seolint.config.ts
import * as config from "seolint/config";
import base from "./seolint.base.ts";
export default config.define({
host: "https://example.com",
presets: [base],
});
A preset can set any config field. Leave host and inputs to each site, unless all sites share them.
Share a preset
To use one preset across many sites, publish it as a package. Export the config, and add seolint as a peer dependency:
// @acme/seolint-preset
import * as config from "seolint/config";
export default config.define({
rules: { "og.*": "error", "link.external.limit": { max: 5 } },
});
{
"name": "@acme/seolint-preset",
"type": "module",
"exports": "./index.js",
"peerDependencies": {
"seolint": "*"
}
}
Publish JavaScript, or TypeScript types with it. Node does not strip types from files in node_modules.
Add plugins
A preset can include plugins. Configs that use the preset run them, and can set their rules:
// @acme/seolint-preset
import * as config from "seolint/config";
const brand = config.plugin({
name: "brand",
rules: { "brand.title": { name: "" } },
run(context, document) {
// ...
},
});
export default config.define({
plugins: [brand],
rules: { "brand.title": { name: "Acme" } },
});
// seolint.config.ts
import * as config from "seolint/config";
import acme from "@acme/seolint-preset";
export default config.define({
presets: [acme],
rules: { "brand.title": { name: "Acme Docs" } },
});
define() remembers the plugins of a config, so the config that uses it type-checks their rules too. Here, "brand.title": { name: 1 } is a type error.
A plugin replaces an earlier plugin with the same name, so two presets can include the same plugin. Two plugins with different names cannot declare the same rule.