Getting Started
Components
Search for a command to run...
The @strongtie code quality packages provide pre-configured linting and formatting for Simpson Strong-Tie projects. They extend Ultracite with company-specific rule overrides.
ESLint 9 Required: Due to compatibility issues with eslint-plugin-react
and other plugins, ESLint 10 is not yet supported. These packages require
ESLint 9.x.
| Package | Description |
|---|---|
@strongtie/eslint | ESLint rules for TypeScript, React, and accessibility |
@strongtie/prettier | Prettier config (re-exports Ultracite) |
@strongtie/stylelint | Stylelint config with Tailwind CSS support |
tsconfig.jsonThe packages are hosted on Azure Artifacts (Simpson feed). Create a .npmrc file in your project root:
@strongtie:registry=https://pkgs.dev.azure.com/StrongTie/_packaging/Simpson/npm/registry/
@strongtie:always-auth=true
registry=https://registry.npmjs.org/Important: Use @strongtie:registry (scoped) to ensure only
@strongtie/* packages use Azure Artifacts authentication.
First, install Ultracite and pin ESLint to version 9:
Critical: You must use ESLint 9.x (^9), not ESLint 10. The
eslint-plugin-react package does not yet support ESLint 10, which will cause
runtime errors.
Create the following configuration files in your project root:
ESLint Configuration:
import base from "@strongtie/eslint/base"
import react from "@strongtie/eslint/react"
import { defineConfig } from "eslint/config"
export default defineConfig([
// Global ignores
{
ignores: [
"**/dist/",
"**/build/",
"**/.next/",
"**/.turbo/",
"**/node_modules/",
"**/*.config.ts",
"**/*.config.mjs",
"**/*.d.ts",
"**/*.json",
"components/ui/**/*",
],
},
// Base TypeScript config (auto-detects ./tsconfig.json)
...base(),
// React/JSX config
...react,
])Prettier Configuration:
export { default } from "@strongtie/prettier"Stylelint Configuration:
export { default } from "@strongtie/stylelint"Create .prettierignore in your project root:
# Build outputs
dist/
build/
# Test coverage
coverage/
# Dependencies
node_modules/
.yalc/
# Config files
*.config.ts
*.config.mjs
*.config.js
# Type definitions
**/*.d.ts
# JSON files
**/*.json
# UI components (shadcn/ui)
components/ui/Create .stylelintignore in your project root:
# Build outputs
dist/
build/
# Coverage
coverage/
# Dependencies
node_modules/
.yalc/
# Non-CSS files
**/*.ts
**/*.tsx
**/*.js
**/*.jsx
**/*.mjs
**/*.cjs
**/*.json
**/*.md
# Config files
*.config.*
# UI components (shadcn/ui)
components/ui/{
"scripts": {
"check": "ultracite check",
"fix": "ultracite fix",
"lint": "eslint .",
"lint:fix": "eslint --fix .",
"lint:css": "stylelint '**/*.css'",
"lint:css:fix": "stylelint '**/*.css' --fix",
"format": "prettier --check .",
"format:fix": "prettier --write ."
}
}# Check for issues without fixing
npm run check
# Auto-fix issues
npm run fix
# Run ESLint
npm run lint
# Run Stylelint
npm run lint:css
# Check formatting
npm run formatFor monorepos or custom tsconfig locations, pass the project option:
import base from "@strongtie/eslint/base"
import react from "@strongtie/eslint/react"
export default [
{ ignores: ["**/dist/", "**/node_modules/"] },
// Monorepo with multiple tsconfig files
...base({
project: ["./apps/*/tsconfig.json", "./packages/*/tsconfig.json"],
}),
...react,
]For build scripts and tooling that run in Node.js:
import base from "@strongtie/eslint/base"
import react from "@strongtie/eslint/react"
import scripts from "@strongtie/eslint/scripts"
export default [
{ ignores: ["**/dist/"] },
...base(),
...react,
...scripts, // Relaxes rules for scripts/**/*.{js,mjs,ts}
]For Vite projects, add the react-refresh plugin:
import base from "@strongtie/eslint/base"
import react from "@strongtie/eslint/react"
import reactRefresh from "eslint-plugin-react-refresh"
import { defineConfig } from "eslint/config"
export default defineConfig([
{ ignores: ["**/dist/"] },
...base(),
...react,
{
plugins: {
"react-refresh": reactRefresh,
},
rules: {
"react-refresh/only-export-components": [
"warn",
{ allowConstantExport: true },
],
},
},
])You can also use named imports:
import { base, react, scripts } from "@strongtie/eslint"
export default [...base(), ...react, ...scripts]The package relaxes several strict TypeScript rules for practical development:
| Rule | Setting | Reason |
|---|---|---|
@typescript-eslint/no-unsafe-* | off | Too strict for real-world code |
@typescript-eslint/no-floating-promises | off | Common in React Query callbacks |
@typescript-eslint/no-non-null-assertion | warn | Can cause runtime errors |
@typescript-eslint/no-shadow | warn | Variable shadowing causes confusion |
@typescript-eslint/prefer-nullish-coalescing | off | || is often intentional for falsy coalescing |
@typescript-eslint/use-unknown-in-catch-callback-variable | off | Too strict for practical catch handling |
| Rule | Setting | Reason |
|---|---|---|
react/jsx-indent* | off | Handled by Prettier/Biome |
react/jsx-no-constructed-context-values | off | React 19 Compiler handles memoization |
react/function-component-definition | off | Next.js uses export default function |
react-hooks/purity | off | Too strict for practical use |
react-hooks/todo | off | React Compiler not yet adopted |
react-hooks/immutability | off | React Compiler not yet adopted |
| Rule | Setting | Reason |
|---|---|---|
jsx-a11y/prefer-tag-over-role | warn | Encourages semantic HTML over ARIA |
| Rule | Setting | Reason |
|---|---|---|
no-console | warn | Allowed in development |
no-nested-ternary | off | Sometimes clearer than if/else |
unicorn/filename-case | off | Existing codebase conventions |
unicorn/number-literal-case | off | Conflicts with Prettier |
unicorn/no-await-expression-member | off | (await foo).bar is valid |
sonarjs/cognitive-complexity | off | Handled elsewhere |
sonarjs/different-types-comparison | off | SSR check pattern is valid |
logical-assignment-operators | off | Stylistic preference |
no-await-in-loop | off | Sometimes necessary |
The @strongtie/eslint package includes custom ESLint rules:
| Rule | Setting | Reason |
|---|---|---|
@strongtie/no-static-inline-style | error | Use Tailwind or CSS modules instead of inline styles |
// ❌ Bad - static inline styles
<div style={{ color: "red" }} />
<div style={{ margin: 0, padding: 10 }} />
// ✅ OK - dynamic content (allowed)
<div style={{ color: dynamicColor }} />
<div style={{ width: `${size}px` }} />
<div style={styleObject} />| Rule | Setting | Reason |
|---|---|---|
at-rule-no-unknown | Allows Tailwind | Supports @tailwind, @apply, @layer, etc. |
For monorepos using Turbo:
{
"tasks": {
"lint": {
"dependsOn": ["^lint"],
"outputs": []
},
"check": {
"outputs": []
},
"fix": {
"outputs": []
}
}
}Each workspace package should have its own config files or inherit from the root.
If you see this error:
TypeError: Error while loading rule 'react/boolean-prop-naming': contextOrFilename.getFilename is not a function
You're using ESLint 10. Downgrade to ESLint 9:
If you see Cannot find package 'eslint-plugin-*' errors, install the missing peer dependencies from Step 4 above.
Ensure your tsconfig.json includes all files being linted:
{
"include": ["src/**/*", "**/*.ts", "**/*.tsx"]
}If npm run lint fails with "command not found":
If Stylelint reports errors on non-CSS files, ensure your .stylelintignore excludes them (see Step 7 above).