Hello! Welcome back to our course on developing a modern JavaScript framework.
In our last lesson, we successfully initialized our TypeScript project and installed BiomeJS, creating a default biome.json configuration file. We now have a solid foundation to build upon.
This lesson focuses on tailoring Biome to our project's specific needs. We will configure its formatter and linter rules, which is the core of establishing a consistent and high-quality codebase. Since you have extensive experience with ESLint and Prettier, we'll frequently draw parallels to help you map your existing knowledge to Biome's configuration system.
By the end of this lesson, you will be able to configure BiomeJS linter and formatter rules to enforce a specific coding standard for our project.
1. Understanding the biome.json Structure
The biome.json file is the single source of truth for all of Biome's functionality. Before we start changing things, let's get a high-level overview of its structure. The main components you'll work with are formatter, linter, and language-specific settings under a javascript key.
The following video provides an excellent walkthrough of the biome.json file, explaining how these different sections work together.
I Will Never Use Prettier or ESLint Again
To begin, let's watch this segment from the "I Will Never Use Prettier or ESLint Again" video by Web Dev Simplified. It gives a clear, practical tour of the configuration file.
Watch the configuration process. Pay close attention to how the configuration is broken down into formatter, linter, and the javascript section for language-specific rules. This will give you a mental model for the rest of the lesson.
As you saw, the configuration is neatly organized, allowing for both global and language-specific settings.
2. Configuring the Formatter (The "Prettier" Part)
Biome's formatter is its equivalent of Prettier. It ensures your code has a consistent style, handling things like indentation, line width, and quotes. Let's establish a style guide for our framework project. We'll use:
- Indentation: 2 spaces
- Line Width: 100 characters
- Quote Style: Single quotes
- Semicolons: Only when necessary
Open your biome.json file and replace its content with the following configuration. This sets up the formatter according to our new style guide.
{
"$schema": "https://biomejs.dev/schemas/1.8.3/schema.json",
"formatter": {
"enabled": true,
"indentStyle": "space",
"indentWidth": 2,
"lineWidth": 100
},
"javascript": {
"formatter": {
"quoteStyle": "single",
"semicolons": "asNeeded",
"trailingCommas": "all"
}
},
"linter": {
"enabled": true,
"rules": {
"recommended": true
}
},
"organizeImports": {
"enabled": true
}
}
Notice how global settings like indentWidth are in the top-level formatter object, while JavaScript-specific rules like quoteStyle are nested under the javascript.formatter object.
For a complete list of all available formatter options, the official Biome documentation is the definitive source.
The official Biome documentation provides a comprehensive reference for all configuration options. It's a valuable resource to consult when you want to fine-tune your setup.
Briefly look over the formatter and javascript.formatter sections in the documentation. You don't need to memorize everything, but it's good to know what's possible, such as options for bracket spacing and arrow function parentheses.
3. Configuring the Linter (The "ESLint" Part)
Biome's linter is its equivalent of ESLint. It analyzes your code for potential bugs, stylistic issues, and security vulnerabilities.
In our biome.json, the line "recommended": true under linter.rules is analogous to extends: 'eslint:recommended'. It enables a curated set of essential rules.
Your experience with ESLint has likely taught you that no single ruleset is perfect for every project. You often need to disable certain rules or change their severity ("error", "warn", "off"). Biome handles this in a very similar way.
Rules are organized into groups like correctness, style, and suspicious. You can customize rules within these groups. For example, during early development, you might want to disable the rule that flags unused variables.
Getting Started with BiomeJS | Better Stack Community
This article from Better Stack Community gives a concise example of how to disable a specific linter rule.
Read the "Linter" section. Focus on the example that shows how to disable the noUnusedVariables rule by setting its value to "off" within its group (correctness).
Let's apply this. For our project, we'll keep the recommended rules but turn off noUnusedVariables for now to facilitate easier prototyping.
Update the linter section in your biome.json:
// ... inside biome.json
"linter": {
"enabled": true,
"rules": {
"recommended": true,
"correctness": {
"noUnusedVariables": "off"
}
}
},
// ...
This layered approach—starting with a recommended set and then overriding specific rules—is a powerful pattern you'll be familiar with from ESLint.
4. File-Specific Rules with overrides
A common requirement in complex projects is to have different rules for different types of files. For instance, testing files often have different complexity or dependency patterns than application source code. ESLint handles this with an overrides array in its configuration. Biome provides the exact same capability.
Let's imagine we want to allow debugger statements in our test files but not in our main source code. We can achieve this with an overrides block.
The official documentation explains how to use the overrides feature to apply specific configurations to files matching a glob pattern.
Review the overrides section. Note the structure: it's an array of objects, where each object specifies includes (an array of glob patterns) and the specific linter or formatter settings to apply.
Let's add an override to our biome.json to disable the noDebugger rule for any files ending in .test.ts.
Add the overrides array to the top level of your biome.json:
{
"$schema": "https://biomejs.dev/schemas/1.8.3/schema.json",
"formatter": {
// ...
},
"javascript": {
// ...
},
"linter": {
// ...
},
"organizeImports": {
// ...
},
"overrides": [
{
"includes": ["**/*.test.ts"],
"linter": {
"rules": {
"suspicious": {
"noDebugger": "warn"
}
}
}
}
]
}
Here, we've set noDebugger to "warn" for test files, so it will show a warning instead of an error, allowing builds to pass while still reminding us to remove it.
5. Applying Your New Configuration
Now that we've defined our rules, let's see them in action.
1. Add a Run Script:
First, let's add a convenient script to our package.json to run Biome across our project.
// In package.json
"scripts": {
"format": "biome format --write .",
"lint": "biome lint --apply ."
},
biome format --write .: Finds all applicable files and applies formatter rules.biome lint --apply .: Finds all linting issues and applies safe fixes.
2. Create a Test File:
Create a new file src/index.ts and add the following intentionally messy code:
// src/index.ts
var message = "hello world"
function Greet(person:string, date:Date) {
console.log(`Hello ${person}, today is ${date.toDateString()}!`);
}
Greet("Brendan", new Date());
const unused = 123;
This code violates several of our rules:
- It uses
varinstead ofconst. - It has inconsistent spacing.
- It uses double quotes instead of single quotes.
- The function
Greetis not capitalized correctly (PascalCase is a common convention for types/classes, but camelCase for functions). Biome'sstyle/camelCaserule will catch this. - It has an unused variable (
unused), but we've turned that rule off.
3. Run the Linter:
Now, run the lint command from your terminal:
npm run lint
# or
yarn lint
You should see output where Biome identifies issues and automatically fixes many of them. Your src/index.ts file should now look like this:
// src/index.ts
const message = 'hello world'
function greet(person: string, date: Date) {
console.log(`Hello ${person}, today is ${date.toDateString()}!`)
}
greet('Brendan', new Date())
const unused = 123
The code is now formatted correctly, var is replaced with const, and the function name is corrected to greet. The unused variable remains, as we configured the linter to ignore it.
Conclusion
In this lesson, you've taken a default Biome setup and customized it to enforce a professional coding standard. You've seen how your existing mental models from ESLint and Prettier translate directly to Biome's configuration.
Key Takeaways:
- Biome's configuration is managed in a single
biome.jsonfile. - The
formattersection is the equivalent of Prettier, controlling code style. - The
lintersection is the equivalent of ESLint, controlling code quality and correctness. - Rules are grouped logically (e.g.,
correctness,style), and you can start with therecommendedset and override specific rules. - The
overridesblock provides a powerful, familiar way to apply different rules to different sets of files.
Next Lesson Preview:
Having a great configuration is one thing, but integrating it seamlessly into your daily workflow is what makes it truly powerful. In the next lesson, we will integrate BiomeJS with VS Code for real-time feedback and set up pre-commit hooks to automatically enforce our rules before any code is checked in.
Can't find a good explanation? Sign up and we'll make it for you
Sign up