eslint-plugin-code-style
v3.4.2
Published
A custom ESLint plugin for enforcing consistent code formatting and style rules in React/JSX projects
Downloads
370
Maintainers
Readme
ESLint Plugin Code Style
A powerful ESLint plugin for enforcing consistent code formatting and style rules in React/JSX projects.
83 rules (72 auto-fixable, 24 configurable) to keep your codebase clean and consistent
🎯 Why This Plugin?
This plugin provides 83 custom rules (72 auto-fixable, 24 configurable) for code formatting. Built for ESLint v9+ flat configs (v9 and v10 supported).
Note: ESLint deprecated 79 formatting rules in v8.53.0. Our recommended configs use
@stylistic/eslint-pluginas the replacement for these deprecated rules.
Key Benefits:
- Fills the gaps — Provides formatting rules not available in other plugins
- Works alongside existing tools — Complements ESLint's built-in rules and packages like eslint-plugin-react, eslint-plugin-import, etc
- Self-sufficient rules — Each rule handles complete formatting independently
- Consistency at scale — Reduces code-style differences between team members by enforcing uniform formatting across your projects
- Highly automated — 72 of 83 rules support auto-fix with
eslint --fix - Next.js compatible — Rules automatically detect and respect App Router conventions (dynamic routes, reserved filenames, route groups, parallel routes)
When combined with ESLint's native rules and other popular plugins, this package helps create a complete code style solution that keeps your codebase clean and consistent.
Documentation • Installation • Quick Start • Recommended Configs • Rules • Contributing
🗂️ Built for Type-Based Folder Structure
This plugin is designed around a type-based folder structure — source files grouped by their role, not by feature. It works best, and many rules auto-fix correctly, when your project organizes code into folders like:
src/
├── components/ ├── hooks/ ├── services/
├── atoms/ ├── providers/ ├── middlewares/
├── views/ ├── constants/ ├── helpers/
├── pages/ ├── data/ └── ...Several rules look at the folder a file lives in and build their expectations from it:
folder-based-naming-convention— applies the right suffix per folder (views/→*View,layouts/→*Layout,pages/→*Page,providers/→*Provider), camelCase fordata//constants/, and so on.folder-structure-consistency/no-redundant-folder-suffix/no-scattered-component-variants— enforce consistent flat-vs-wrapped module folders, stop redundantbutton/button.tsxstyle naming, and collapse scattered variant families (copy-button.tsx+icon-button.tsx→button/{copy,icon}.tsx).hook-file-naming-convention/index-exports-only/module-index-exports— expect hooks andindexbarrels to follow the module folder they sit in.function-naming-convention/variable-naming-convention— path-scoped naming (e.g. component casing undercomponents/, verb-first handlers, hook prefixes).
Recommendation: adopt this type-based structure (components, atoms, views, pages, hooks, providers, services, middlewares, …). The closer your project follows it, the more the plugin enforces and fixes automatically. Rules that don't depend on folders (spacing, JSX, imports, formatting) work the same in any layout.
📁 Recommended Configurations
We provide ready-to-use ESLint flat configuration files that combine eslint-plugin-code-style with carefully selected third-party plugins and ESLint built-in rules. These configurations represent our battle-tested setup that reduces code-style differences by ~95%.
💡 Why Use These Configs?
- Complete Coverage — Combines ESLint built-in rules, third-party plugins, and all 83 code-style rules
- Ready-to-Use — Copy the config file and start linting immediately
- Battle-Tested — These configurations have been refined through real-world usage
- Fully Documented — Each config includes detailed instructions and explanations
📋 Available Configurations
Configs are organized by ESLint version:
ESLint v9 (recommended-configs/v9/)
| Configuration | Description | Link | |---------------|-------------|------| | React | React.js projects (JavaScript, JSX) | View Config | | React + TypeScript | React + TypeScript projects | View Config | | React + Tailwind | React + Tailwind CSS projects | View Config | | React + TS + Tailwind | React + TypeScript + Tailwind CSS | View Config |
ESLint v10 (recommended-configs/v10/)
| Configuration | Description | Link | |---------------|-------------|------| | React | React.js projects (JavaScript, JSX) | View Config | | React + TypeScript | React + TypeScript projects | View Config | | React + Tailwind | React + Tailwind CSS projects | View Config | | React + TS + Tailwind | React + TypeScript + Tailwind CSS | View Config |
ESLint v10 note: The v10 configs replace
eslint-plugin-reactandeslint-plugin-react-hookswith@eslint-react/eslint-plugin, and removeeslint-plugin-jsx-a11y(no v10-compatible version available).
⚡ Quick Start with Recommended Config
- Navigate to the recommended-configs folder
- Choose
v9/orv10/based on your ESLint version - Choose the configuration for your project type
- Follow the installation instructions in the README
- Copy the
eslint.config.jsto your project root - Run
eslint src/ --fix
Note: Each configuration includes a detailed README with installation commands, plugin explanations, and rule documentation.
✨ Features
🔧 Auto-Fixable Rules
72 rules support automatic fixing with eslint --fix. 24 rules have configurable options. 11 rules are report-only (require manual changes).
⚛️ React & JSX Support
Built specifically for React projects with comprehensive JSX formatting rules.
✅ ESLint v9 & v10 Ready
Designed for ESLint's flat config system. Supports both ESLint v9 and v10 with dedicated recommended configurations.
📭 Zero Dependencies
Lightweight plugin with no external dependencies. Fast and efficient.
🔄 Next.js Compatible
Our rules auto-detect Next.js per file (no config) — they respect App Router conventions: the app/ folder, reserved filenames (page, layout, route, …), reserved exports (generateMetadata, route handlers), framework prop names, and special folders ([slug], (group), @parallel, _private). Two third-party rules (import-x/no-default-export, check-file/folder-naming-convention) still need an opt-in block — see the commented "Next.js App Router (opt-in)" section at the bottom of every recommended config.
📦 TypeScript First
9 dedicated TypeScript rules for enums, interfaces, types, prop naming, and type annotation formatting.
📦 Installation
# npm
npm install eslint-plugin-code-style --save-dev
# pnpm
pnpm add eslint-plugin-code-style -D
# yarn
yarn add eslint-plugin-code-style -D📋 Requirements
| Dependency | Version |
|------------|---------|
| ESLint | >= 9.0.0 |
| Node.js | >= 20.0.0 |
🚀 Quick Start
Create or update your eslint.config.js:
import codeStyle from "eslint-plugin-code-style";
export default [
{
plugins: {
"code-style": codeStyle,
},
rules: {
// Enable individual rules
"code-style/import-format": "error",
"code-style/jsx-children-on-new-line": "error",
// ... add more rules as needed
},
},
];Then run ESLint with auto-fix:
eslint src/ --fix📋 Enable All Rules
rules: {
"code-style/absolute-imports-only": "error",
"code-style/array-callback-destructure": "error",
"code-style/array-items-per-line": "error",
"code-style/array-objects-on-new-lines": "error",
"code-style/arrow-function-block-body": "error",
"code-style/arrow-function-simple-jsx": "error",
"code-style/arrow-function-simplify": "error",
"code-style/assignment-value-same-line": "error",
"code-style/block-statement-newlines": "error",
"code-style/class-method-definition-format": "error",
"code-style/class-naming-convention": "error",
"code-style/classname-dynamic-at-end": "error",
"code-style/classname-multiline": "error",
"code-style/classname-no-extra-spaces": "error",
"code-style/classname-order": "error",
"code-style/comment-format": "error",
"code-style/component-props-destructure": "error",
"code-style/component-props-inline-type": "error",
"code-style/svg-icon-naming-convention": "error",
"code-style/curried-arrow-same-line": "error",
"code-style/empty-line-after-block": "error",
"code-style/enum-format": "error",
"code-style/enum-type-enforcement": "error",
"code-style/export-format": "error",
"code-style/folder-based-naming-convention": "error",
"code-style/folder-structure-consistency": "error",
"code-style/no-redundant-folder-suffix": "error",
"code-style/function-arguments-format": "error",
"code-style/function-call-spacing": "error",
"code-style/function-declaration-style": "error",
"code-style/function-naming-convention": "error",
"code-style/function-object-destructure": "error",
"code-style/function-params-per-line": "error",
"code-style/hook-callback-format": "error",
"code-style/hook-deps-per-line": "error",
"code-style/hook-file-naming-convention": "error",
"code-style/hook-function-naming-convention": "error",
"code-style/use-state-naming-convention": "error",
"code-style/if-else-spacing": "error",
"code-style/if-statement-format": "error",
"code-style/import-format": "error",
"code-style/import-source-spacing": "error",
"code-style/index-export-style": "error",
"code-style/index-exports-only": "error",
"code-style/inline-export-declaration": "error",
"code-style/interface-format": "error",
"code-style/jsx-children-on-new-line": "error",
"code-style/jsx-closing-bracket-spacing": "error",
"code-style/jsx-element-child-new-line": "error",
"code-style/jsx-logical-expression-simplify": "error",
"code-style/jsx-parentheses-position": "error",
"code-style/jsx-prop-naming-convention": "error",
"code-style/jsx-simple-element-one-line": "error",
"code-style/jsx-string-value-trim": "error",
"code-style/jsx-ternary-format": "error",
"code-style/logical-expression-multiline": "error",
"code-style/member-expression-bracket-spacing": "error",
"code-style/module-index-exports": "error",
"code-style/multiline-if-conditions": "error",
"code-style/nested-call-closing-brackets": "error",
"code-style/no-empty-lines-in-arrays": "error",
"code-style/no-empty-lines-in-function-calls": "error",
"code-style/no-empty-lines-in-function-params": "error",
"code-style/no-empty-lines-in-jsx": "error",
"code-style/no-empty-lines-in-objects": "error",
"code-style/no-empty-lines-in-switch-cases": "error",
"code-style/no-hardcoded-strings": "error",
"code-style/no-inline-type-definitions": "error",
"code-style/object-property-per-line": "error",
"code-style/object-property-value-brace": "error",
"code-style/object-property-value-format": "error",
"code-style/opening-brackets-same-line": "error",
"code-style/prop-naming-convention": "error",
"code-style/react-code-order": "error",
"code-style/simple-call-single-line": "error",
"code-style/single-argument-on-one-line": "error",
"code-style/string-property-spacing": "error",
"code-style/ternary-condition-multiline": "error",
"code-style/type-annotation-spacing": "error",
"code-style/type-format": "error",
"code-style/typescript-definition-location": "error",
"code-style/variable-naming-convention": "error",
}📖 Rules Categories
83 rules total — 72 with auto-fix 🔧, 24 configurable ⚙️, 11 report-only
📖 Full documentation with examples: www.eslint-plugin-code-style.org • Local docs
Legend: 🔧 Auto-fixable with
eslint --fix• ⚙️ Customizable options
| Rule | Description |
|------|-------------|
| Array Rules | |
| array-callback-destructure | Destructured params in array callbacks (map, filter, find) go multiline when ≥2 properties 🔧 |
| array-items-per-line | Collapse arrays ≤ threshold to one line; expand larger arrays with each item on own line (default: ≤3) 🔧 ⚙️ |
| array-objects-on-new-lines | Each object in an array starts on its own line for better visual scanning 🔧 |
| no-empty-lines-in-arrays | Disallow blank lines inside array literals — after [, before ], and between elements 🔧 |
| Arrow Function Rules | |
| arrow-function-block-body | Wrap multiline arrow function expressions in parentheses for clear boundaries 🔧 |
| arrow-function-simple-jsx | Collapse arrow functions returning simple single-element JSX to one line, remove unnecessary parens 🔧 |
| arrow-function-simplify | Convert block body with single return to implicit return: () => { return x; } → () => x 🔧 |
| curried-arrow-same-line | Curried arrow functions start on same line as =>, not on new line 🔧 |
| Call Expression Rules | |
| function-arguments-format | Args ≥ threshold or multiline: first arg on new line, each on own line, closing ) on new line (default: ≥2) 🔧 ⚙️ |
| nested-call-closing-brackets | Chain closing brackets on same line: })); not scattered across lines 🔧 |
| no-empty-lines-in-function-calls | No empty lines between arguments or after (/before ) 🔧 |
| opening-brackets-same-line | Opening {, [, or ( on same line as function call, not on new line 🔧 |
| simple-call-single-line | Collapse simple arrow function calls to single line (including callbacks with params and optional chaining) 🔧 |
| single-argument-on-one-line | Single simple argument stays on one line: fn(x) not expanded 🔧 |
| Comment Rules | |
| comment-format | Space after //, space inside /* */, convert single-line blocks to //, no blank lines between file-top comments 🔧 |
| Component Rules | |
| component-props-destructure | Component props must be destructured ({ prop }) not received as (props) 🔧 |
| component-props-inline-type | Inline type annotation } : { with matching props, proper spacing, commas, no interface reference 🔧 |
| folder-based-naming-convention | Enforce naming based on folder: suffix for views/layouts/pages/providers/reducers/contexts/themes, camelCase suffix for data/constants/strings/services/reducers folders, chained folder names for nested files, singularizes plural folders 🔧 ⚙️ |
| folder-structure-consistency | Enforce consistent folder structure (flat vs wrapped) in module folders (atoms, components, hooks, enums, views, etc.) ⚙️ |
| no-redundant-folder-suffix | Disallow file and folder names that redundantly include the parent folder name as a suffix |
| no-scattered-component-variants | Disallow scattered component variants that share a trailing name token — collapse them into one folder named by the shared token ⚙️ |
| svg-icon-naming-convention | SVG components must end with "Icon" suffix; "Icon" suffix components must return SVG |
| Class Rules | |
| class-method-definition-format | Consistent spacing in class/method definitions: space before {, no space before ( 🔧 |
| class-naming-convention | Class declarations must end with "Class" suffix (e.g., ApiServiceClass) 🔧 |
| Control Flow Rules | |
| block-statement-newlines | Newline after { and before } in if/for/while/function blocks 🔧 |
| empty-line-after-block | Empty line required between closing } of block and next statement 🔧 |
| if-else-spacing | Empty line between consecutive if blocks, no empty line between single-line if/else 🔧 |
| if-statement-format | { on same line as if/else if, else on same line as }, proper spacing 🔧 |
| logical-expression-multiline | Logical expressions (&&, ||) with >maxOperands get one operand per line (default: >3) 🔧 ⚙️ |
| multiline-if-conditions | Conditions exceeding threshold get one operand per line with proper indentation (default: >3) 🔧 ⚙️ |
| no-empty-lines-in-switch-cases | No empty line after case X: before code, no empty lines between cases 🔧 |
| ternary-condition-multiline | ≤maxOperands always single line; >maxOperands multiline (based on operand count, not line length) 🔧 ⚙️ |
| Function Rules | |
| function-call-spacing | No space between function name and (: fn() not fn () 🔧 |
| function-declaration-style | Auto-fix for func-style: converts function declarations to arrow expressions 🔧 |
| function-naming-convention | Functions use camelCase, start with verb, end with Handler suffix; handleXxx → xxxHandler 🔧 |
| function-object-destructure | Non-component functions: use typed params (not destructured), destructure in body; report dot notation access. Module imports default to dot notation with JSX exception (configurable: smart/strict-dot/destructure) 🔧 ⚙️ |
| function-params-per-line | When multiline, each param on own line with consistent indentation 🔧 |
| no-empty-lines-in-function-params | No empty lines between parameters or after (/before ) 🔧 |
| Hook Rules | |
| hook-callback-format | React hooks: callback on new line, deps array on separate line, proper indentation 🔧 |
| hook-deps-per-line | Collapse deps ≤ threshold to one line; expand larger arrays with each dep on own line (default: >2) 🔧 ⚙️ |
| hook-file-naming-convention | Hook files in module subfolders must include the module name (verb hooks: use-{verb}-{module}, list hooks: use-{module}-list) |
| hook-function-naming-convention | Hook function name must match camelCase of file name (e.g., use-create-super-admin.ts → useCreateSuperAdmin) 🔧 |
| use-state-naming-convention | Boolean useState variables must start with is/has/with/without prefix 🔧 ⚙️ |
| Import/Export Rules | |
| absolute-imports-only | Use alias imports from index files only (not deep paths), no relative imports; files within the same module folder must use relative imports — auto-fixes absolute imports to relative (default: @/) 🔧 ⚙️ |
| export-format | export { on same line; collapse ≤ threshold to one line; expand larger with each specifier on own line (default: ≤3) 🔧 ⚙️ |
| import-format | import { and } from on same line; collapse ≤ threshold; expand larger with each specifier on own line (default: ≤3) 🔧 ⚙️ |
| import-source-spacing | No leading/trailing spaces inside import path quotes 🔧 |
| index-export-style | Index files: no blank lines, enforce shorthand or import-export style; Regular files: require blank lines between exports (default: shorthand) 🔧 ⚙️ |
| index-exports-only | Index files should only contain imports and re-exports, not code definitions (types, functions, variables, classes) |
| inline-export-declaration | Enforce inline export declarations instead of grouped export statements in non-index files 🔧 ⚙️ |
| module-index-exports | Index files must export all folder contents (files and subfolders) ⚙️ |
| JSX Rules | |
| classname-dynamic-at-end | Dynamic expressions (${className}) must be at the end of class strings (JSX and variables) 🔧 |
| classname-multiline | Long className strings broken into multiple lines; smart detection for objects/returns with Tailwind values 🔧 ⚙️ |
| classname-no-extra-spaces | No extra/leading/trailing spaces in class strings; smart detection for objects/returns with Tailwind values 🔧 |
| classname-order | Tailwind class ordering in variables/objects/returns; smart detection for Tailwind values 🔧 |
| jsx-children-on-new-line | Multiple JSX children: each on own line with proper indentation 🔧 |
| jsx-closing-bracket-spacing | No space before > or /> in JSX tags 🔧 |
| jsx-element-child-new-line | Nested JSX elements on new lines; text/expression children can stay inline 🔧 |
| jsx-logical-expression-simplify | Remove unnecessary parens around conditions and JSX in logical expressions 🔧 |
| jsx-parentheses-position | Opening ( for multiline JSX on same line as return/=>, not on new line 🔧 |
| jsx-prop-naming-convention | Props: camelCase for regular, kebab-case for data-/aria-, PascalCase for component refs |
| jsx-simple-element-one-line | Collapse simple JSX with single text/expression child to one line 🔧 |
| jsx-string-value-trim | No leading/trailing whitespace inside JSX string attribute values 🔧 |
| jsx-ternary-format | Simple ternaries on one line; complex branches get parens with proper indentation 🔧 |
| no-empty-lines-in-jsx | No empty lines between children or after opening/before closing tags 🔧 |
| Object Rules | |
| no-empty-lines-in-objects | No empty lines between properties or after {/before } 🔧 |
| object-property-per-line | Collapse ≤ threshold to one line; expand larger with {/} on own lines and each property on own line (default: ≥2) 🔧 ⚙️ |
| object-property-value-brace | Opening { of object value on same line as :, not on new line 🔧 |
| object-property-value-format | Simple property values on same line as :, not on new line 🔧 |
| string-property-spacing | No leading/trailing spaces inside string property keys 🔧 |
| Spacing Rules | |
| assignment-value-same-line | Assignment values start on same line as =, not on new line 🔧 |
| member-expression-bracket-spacing | No spaces inside brackets in computed member expressions: arr[0] not arr[ 0 ] 🔧 |
| TypeScript Rules | |
| enum-format | Enforce enum naming (PascalCase + Enum suffix), UPPER_CASE members, no empty lines, and trailing commas 🔧 |
| enum-type-enforcement | Enforce using enum values instead of string literals for variables typed with *Type (e.g., use ButtonVariantEnum.PRIMARY not "primary") 🔧 |
| interface-format | Enforce interface naming (PascalCase + Interface suffix), camelCase properties, no empty lines, and trailing commas 🔧 |
| no-inline-type-definitions | Inline union types in function params should be extracted to named types ⚙️ |
| prop-naming-convention | Enforce boolean props start with is/has/with/without, callback props start with on 🔧 ⚙️ |
| type-annotation-spacing | Enforce consistent spacing in type annotations: no space before colon/generic/array brackets, one space after colon 🔧 |
| type-format | Enforce type naming (PascalCase + Type suffix), camelCase properties, union type formatting, and trailing commas 🔧 ⚙️ |
| typescript-definition-location | Enforce TypeScript definitions (interfaces, types, enums) to be in designated folders ⚙️ |
| React Rules | |
| react-code-order | Enforce consistent ordering in components and hooks: props destructure → refs → state → redux → router → context → custom hooks → derived → memo → callback → handlers → effects → return 🔧 |
| String Rules | |
| no-hardcoded-strings | Enforce importing strings from constants/strings modules instead of hardcoding them ⚙️ |
| Variable Rules | |
| variable-naming-convention | camelCase for all variables and constants, PascalCase for components, use prefix for hooks. Supports opt-in path-scoped case allowances (e.g., SCREAMING_SNAKE_CASE in Redux types folders) 🔧 ⚙️ |
📖 Rules Reference
For detailed documentation with examples, configuration options, and best practices for each rule, see the Rules Reference Documentation or the local docs.
| Category | Rules | Highlights | |----------|:-----:|------------| | Arrays | 3 | Callback destructuring, items-per-line, objects-on-new-lines | | Arrow Functions | 4 | Block body, simple JSX, implicit return, curried arrows | | Call Expressions | 6 | Argument formatting, nested brackets, single-line calls | | Classes | 2 | Method formatting, naming conventions | | Comments | 1 | Comment spacing and format | | Components | 6 | Props destructure, folder naming, structure consistency | | Control Flow | 8 | Block newlines, if/else, logical expressions, ternaries | | Functions | 6 | Call spacing, declaration style, naming, params | | Hooks | 5 | Callback format, deps-per-line, file naming, function naming, useState naming | | Imports/Exports | 8 | Absolute imports, format, index exports, module exports | | JSX | 14 | ClassName handling, children, logical expressions | | Objects | 5 | Property formatting, empty lines, string properties | | React | 1 | Component/hook code ordering | | Spacing | 2 | Assignment values, bracket spacing | | Strings | 1 | No hardcoded strings | | TypeScript | 8 | Enum/interface/type formatting, definition location | | Variables | 1 | Naming conventions (camelCase, PascalCase) |
🔧 Auto-fixing
72 of 83 rules support auto-fixing. Run ESLint with the --fix flag:
# Fix all files in src directory
eslint src/ --fix
# Fix specific file
eslint src/components/MyComponent.jsx --fix
# Fix with specific extensions
eslint "src/**/*.{js,jsx,ts,tsx}" --fix
🚫 Disabling Rules
Disable for a specific line:
// eslint-disable-next-line code-style/rule-name
const code = "violates rule";
Disable for an entire file:
/* eslint-disable code-style/rule-name */Disable in configuration:
rules: {
"code-style/rule-name": "off",
}📖 Documentation
Full documentation is available at www.eslint-plugin-code-style.org — including installation guides, configuration examples, and detailed rule references with good/bad code examples.
🤝 Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
- Fork the repository
- Create your feature branch (
git checkout -b feature/amazing-feature) - Build the project (
npm run build) - Test your changes in all 4 test projects (
cd _tests_/v9/react-ts-tw && npm run lint) - Commit your changes (`git commit -m 'Add some amazing feature')
- Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
📄 License
This project is licensed under the MIT License - see the LICENSE file for details.
Copyright (c) 2026 Eslint Plugin Code Style. All rights reserved.
Made with ❤️ by Mohamed Elhawary
