ESLint configuration for Expo and React Native projects with dual format support
npx @tessl/cli install tessl/npm-eslint-config-expo@10.0.0ESLint Config Expo provides comprehensive ESLint configurations specifically designed for Expo and React Native projects. It offers dual configuration format support - both legacy ESLint configuration and modern flat configuration - with platform-specific file extensions, React Native globals, and optimized rules for Expo development.
npm install eslint-config-expo// .eslintrc.js
module.exports = {
extends: ["expo"],
};Package.json configuration:
{
"eslintConfig": {
"extends": ["expo"]
}
}// eslint.config.js
const expoConfig = require("eslint-config-expo/flat");
const { defineConfig } = require("eslint/config");
module.exports = defineConfig([
...expoConfig,
// Your additional configuration
]);// .eslintrc.js
module.exports = {
extends: ["expo"],
rules: {
// Your custom rules
},
};// eslint.config.js
const expoConfig = require("eslint-config-expo/flat");
const { defineConfig } = require("eslint/config");
module.exports = defineConfig([
...expoConfig,
{
rules: {
// Your custom rules
},
},
]);ESLint Config Expo is structured around several key components:
.android.js, .ios.js, .web.js, .native.js file patternsMain ESLint configuration object for traditional ESLint setup.
// default.js - Main export
module.exports = {
extends: string[];
globals: Record<string, boolean | 'readonly'>;
settings: {
'import/extensions': string[];
'import/resolver': {
node: { extensions: string[] };
};
};
overrides: Array<{
files: string[];
env?: Record<string, boolean>;
}>;
};Modern ESLint flat configuration array for ESLint 9+ compatibility.
// flat.js - Flat config export
const flatConfig: ESLintFlatConfig[];
module.exports = flatConfig;
interface ESLintFlatConfig {
files?: string[];
ignores?: string[];
languageOptions?: {
ecmaVersion?: number;
sourceType?: 'script' | 'module';
globals?: Record<string, boolean | 'readonly'>;
parser?: any;
parserOptions?: Record<string, any>;
};
plugins?: Record<string, any>;
rules?: Record<string, any>;
settings?: Record<string, any>;
}Platform-aware file extension handling for legacy configuration.
// utils/extensions.js
const jsExtensions: string[]; // ['.js', '.jsx']
const tsExtensions: string[]; // ['.ts', '.tsx', '.d.ts']
const platformSubextensions: string[]; // ['.android', '.ios', '.web', '.native']
/**
* Generate platform-specific file extensions from base extensions
* @param extensions - Array of base file extensions (e.g., ['.js', '.jsx'])
* @param platformSubextensions - Array of platform prefixes (e.g., ['.android', '.ios'])
* @returns Array of all combinations plus base extensions
*/
function computeExpoExtensions(
extensions: string[],
platformSubextensions: string[]
): string[];Enhanced file extension handling for flat configuration format.
// flat/utils/extensions.js
const jsExtensions: string[]; // ['.cjs', '.mjs', '.js', '.jsx']
const tsExtensions: string[]; // ['.ts', '.tsx', '.d.ts']
const allExtensions: string[]; // Pre-computed extensions including platform variants
/**
* Generate platform-specific file extensions from base extensions
* @param baseExtensions - Array of base file extensions (e.g., ['.js', '.jsx'])
* @param platformSubextensions - Array of platform prefixes (e.g., ['.android', '.ios'])
* @returns Array of all combinations plus base extensions
* Note: Function exists but is not exported from flat utils
*/
function computeExpoExtensions(
baseExtensions: string[],
platformSubextensions: string[]
): string[];
// Note: platformSubextensions array not exported from flat utils
// allExtensions includes all JS/TS extensions with platform-specific variants pre-computedIndividual configuration modules that compose the complete ESLint setup.
// Legacy format modules
require('./utils/core.js'): ESLintConfig; // Core JS/ESLint rules
require('./utils/react.js'): ESLintConfig; // React-specific rules
require('./utils/typescript.js'): ESLintConfig; // TypeScript rules
require('./utils/expo.js'): ESLintConfig; // Expo-specific rules
// Flat format modules
require('./flat/utils/core.js'): ESLintFlatConfig[];
require('./flat/utils/react.js'): ESLintFlatConfig[];
require('./flat/utils/typescript.js'): ESLintFlatConfig[];
require('./flat/utils/expo.js'): ESLintFlatConfig[];
interface ESLintConfig {
extends?: string[];
plugins?: string[];
rules?: Record<string, any>;
settings?: Record<string, any>;
env?: Record<string, boolean>;
globals?: Record<string, boolean | 'readonly'>;
parser?: string;
parserOptions?: Record<string, any>;
ignorePatterns?: string[];
overrides?: Array<{
files: string[];
[key: string]: any;
}>;
}Both configuration formats define React Native and Expo-specific global variables:
const globals = {
__DEV__: 'readonly', // Development flag
ErrorUtils: false, // React Native error utilities
FormData: false, // Form data API
XMLHttpRequest: false, // HTTP request API
alert: false, // Alert dialog
cancelAnimationFrame: false, // Animation frame API
cancelIdleCallback: false, // Idle callback API
clearImmediate: false, // Immediate API
fetch: false, // Fetch API
navigator: false, // Navigator API
process: false, // Node.js process
requestAnimationFrame: false, // Animation frame API
requestIdleCallback: false, // Idle callback API
setImmediate: false, // Immediate API
window: false, // Browser window
'shared-node-browser': true, // Shared globals flag
};Both configurations automatically handle platform-specific file extensions:
.android.js, .android.ts, .android.jsx, .android.tsx.ios.js, .ios.ts, .ios.jsx, .ios.tsx.web.js, .web.ts, .web.jsx, .web.tsx.native.js, .native.ts, .native.jsx, .native.tsxWeb files automatically enable browser environment globals.
The configuration integrates these essential ESLint plugins:
The configuration enforces these Expo-specific ESLint rules:
expo/use-dom-exports: Ensures proper usage of DOM exports in Expo projectsexpo/no-env-var-destructuring: Prevents destructuring of environment variables that may not be available at build timeexpo/no-dynamic-env-var: Prevents dynamic access to environment variables that should be statically analyzable.d.ts files have import ordering rules disabled for better compatibility.
metro.config.js files are configured with Node.js environment for proper Metro bundler integration.
Android build intermediates in android/app/build directories are automatically ignored.
// .eslintrc.js (Legacy)
module.exports = {
extends: ["expo"],
rules: {
"no-console": "warn",
"prefer-const": "error",
},
};// eslint.config.js (Flat)
const expoConfig = require("eslint-config-expo/flat");
module.exports = [
...expoConfig,
{
rules: {
"no-console": "warn",
"prefer-const": "error",
},
},
];// .eslintrc.js (Legacy)
module.exports = {
extends: ["expo"],
overrides: [
{
files: ["*.ios.*"],
rules: {
"no-restricted-imports": ["error", { patterns: ["**/android/**"] }],
},
},
{
files: ["*.android.*"],
rules: {
"no-restricted-imports": ["error", { patterns: ["**/ios/**"] }],
},
},
],
};// .eslintrc.js (Legacy)
module.exports = {
extends: ["expo"],
overrides: [
{
files: ["*.ts", "*.tsx"],
rules: {
"@typescript-eslint/no-unused-vars": "error",
"@typescript-eslint/explicit-function-return-type": "warn",
},
},
],
};