Wednesday, September 6, 2023
Enforcing coding style with @vercel/style-guide
1.7K views

What's @vercel/style-guide? Why would I use it?
@vercel/style-guide
offers predefined configs of the following tools in a single package.
If you are not sure which ESLint rules or plugins to use (there are A LOT!!!) or wish to enforce code quality but not sure where to start, @vercel/style-guide
is a decent drop-in solution.
What's the catch?
@vercel/style-guide
is strict, VERY strict. If you are getting used to the default config provisioned by a project CLI, e.g., Next.js and Vite, it might take you some time to adapt to it.
Also, I found some configs aren't making too much sense in the way they are configured. I found myself fighting against those configs instead of enhancing my code quality. Thus, I have overridden some of the configs to how I feel comfortable. The good news to you is, I'm glad to share them with you.
What I'm currently using
FYI, the following are the config files I'm running in my Next.js project. Feel free to copy and/or customize them according to your needs.
.eslintrc.js
const { resolve } = require('node:path');
const project = resolve(__dirname, 'tsconfig.json');
module.exports = {
root: true,
extends: [
require.resolve('@vercel/style-guide/eslint/browser'),
require.resolve('@vercel/style-guide/eslint/react'),
require.resolve('@vercel/style-guide/eslint/next'),
require.resolve('@vercel/style-guide/eslint/node'),
require.resolve('@vercel/style-guide/eslint/typescript'),
],
parserOptions: { project },
settings: {
'import/resolver': { typescript: { project } },
/**
* enable MUI Joy components to be checked
* @see {@link https://github.com/jsx-eslint/eslint-plugin-jsx-a11y?tab=readme-ov-file#configurations}
*/
'jsx-a11y': {
polymorphicPropName: 'component',
components: {
Button: 'button',
Icon: 'svg',
IconButton: 'button',
Image: 'img',
Input: 'input',
Link: 'a',
List: 'ul',
ListItem: 'li',
ListItemButton: 'button',
ListDivider: 'li',
NextImage: 'img',
NextLink: 'a',
SvgIcon: 'svg',
Textarea: 'textarea',
},
},
},
rules: {
'@typescript-eslint/consistent-type-imports': 'off',
'@typescript-eslint/explicit-function-return-type': 'off',
'@typescript-eslint/no-confusing-void-expression': [
'error',
{ ignoreArrowShorthand: true },
],
'@typescript-eslint/no-shadow': 'off',
'@typescript-eslint/no-misused-promises': [
'error',
{ checksVoidReturn: false },
],
// such that @/* imports will not be considered as external dependencies
'react/function-component-definition': [
'warn',
{
namedComponents: 'arrow-function',
unnamedComponents: 'arrow-function',
},
],
// sort import statements
'import/order': [
'warn',
{
groups: [
'builtin',
'external',
'internal',
'parent',
'sibling',
'index',
],
'newlines-between': 'always',
alphabetize: { order: 'asc' },
},
],
// sort named imports within an import statement
'sort-imports': ['warn', { ignoreDeclarationSort: true }],
},
overrides: [
// Next.js App Router file convention
// Must use default export
{
files: [
'src/app/**/page.tsx',
'src/app/**/layout.tsx',
'src/app/**/not-found.tsx',
'src/app/**/*error.tsx',
'src/app/sitemap.ts',
'src/app/robots.ts',
],
rules: {
'import/no-default-export': 'off',
'import/prefer-default-export': ['error', { target: 'any' }],
},
},
// module declarations
{
files: ['**/*.d.ts'],
rules: { 'import/no-default-export': 'off' },
},
],
};
tsconfig.json
{
"extends": "@vercel/style-guide/typescript",
"compilerOptions": {
"target": "es5",
"lib": ["dom", "dom.iterable", "esnext"],
"allowJs": true,
"noEmit": true,
"module": "esnext",
"moduleResolution": "bundler",
"resolveJsonModule": true,
"isolatedModules": true,
"jsx": "preserve",
"incremental": true,
"plugins": [
{
"name": "next"
}
],
"paths": {
"@/*": ["./src/*"],
"@mui/material/*": ["./node_modules/@mui/joy/*"]
}
},
"include": [
"src/types/*.d.ts",
"next-env.d.ts",
"**/*.ts",
"**/*.tsx",
".next/types/**/*.ts"
],
"exclude": ["node_modules"]
}
For Prettier, I'm not overriding the options in @vercel/style-guide/prettier
, but I've extended it to support Prisma. That being said if you wish to further customize the config, e.g., using double quotes instead of single quotes, you may create the config file in the following way.
.prettierrc.js
const vercelPrettierOptions = require('@vercel/style-guide/prettier');
/** @type {import('prettier').Options} */
module.exports = {
...vercelPrettierOptions,
plugins: ['prettier-plugin-prisma'],
// your options to override Vercel's options
singleQuote: false,
};