Rules for enforcing Qwik-specific code quality patterns and performance optimizations.
Enforces using the classlist prop over importing a classnames helper.
/**
* Enforces using classlist prop over classnames helper
* The classlist prop accepts an object { [class: string]: boolean } like classnames
*/
preferClasslist: Rule;Rule Configuration:
Options Schema:
{
type: 'object',
properties: {
classnames: {
type: 'array',
description: 'An array of names to treat as classnames functions',
default: ['cn', 'clsx', 'classnames'],
items: {
type: 'string',
minItems: 1,
uniqueItems: true
}
}
},
additionalProperties: false
}Usage Example:
// ❌ Invalid: using classnames helper
import classnames from 'classnames';
<div className={classnames({
'active': isActive,
'disabled': isDisabled
})} />
// ✅ Valid: using classlist prop
<div class={{
'active': isActive,
'disabled': isDisabled
}} />Detects declaration location of loader$ functions to ensure proper placement.
/**
* Detects declaration location of loader$ functions
* Ensures loaders are declared in appropriate locations for proper SSR
*/
loaderLocation: Rule;Rule Configuration:
Options Schema:
{
type: 'object',
properties: {
routesDir: {
type: 'string',
default: 'src/routes'
}
},
additionalProperties: false
}Usage Example:
// ❌ Invalid: loader in wrong location
export default component$(() => {
const data = routeLoader$(() => { // Error: loader should be at module level
return { message: 'Hello' };
});
return <div>{data.value.message}</div>;
});
// ✅ Valid: loader at module level
export const useData = routeLoader$(() => {
return { message: 'Hello' };
});
export default component$(() => {
const data = useData();
return <div>{data.value.message}</div>;
});Detects useVisibleTask$() functions and suggests alternatives.
/**
* Detects useVisibleTask$() functions
* Suggests alternatives for better performance and SSR compatibility
*/
noUseVisibleTask: Rule;Rule Configuration:
Usage Example:
// ⚠️ Discouraged: useVisibleTask for simple effects
export default component$(() => {
useVisibleTask$(() => { // Warning: consider alternatives
console.log('Component visible');
});
return <div>Content</div>;
});
// ✅ Better: use useTask$ for most cases
export default component$(() => {
useTask$(() => {
console.log('Component mounted');
});
return <div>Content</div>;
});Classlist vs Classnames:
The prefer-classlist rule encourages using Qwik's built-in classlist prop instead of external classnames libraries:
Benefits of classlist prop:
Pattern Comparison:
// External library approach (discouraged)
import cn from 'classnames';
<div className={cn('base', { active: isActive })} />
// Qwik native approach (preferred)
<div class={['base', { active: isActive }]} />
// or
<div class={{ base: true, active: isActive }} />Module-Level Declaration: Loaders should be declared at the module level for proper SSR and routing integration:
Why Module-Level:
Pattern Examples:
// ❌ Component-level (runtime creation)
export default component$(() => {
const loader = routeLoader$(() => getData()); // Problem: dynamic creation
return <div>{loader.value}</div>;
});
// ✅ Module-level (static analysis)
export const usePageData = routeLoader$(() => getData());
export default component$(() => {
const data = usePageData(); // Proper: static reference
return <div>{data.value}</div>;
});Performance Considerations:
useVisibleTask$ runs when a component becomes visible, but often other hooks are more appropriate:
Alternative Patterns:
// For initialization effects
useTask$(() => {
// Runs on component mount (SSR + client)
});
// For resource loading
useResource$(async () => {
// Lazy resource loading with proper SSR handling
});
// For browser-only effects
useVisibleTask$(({ track }) => {
// Only when you specifically need visibility-based triggering
track(() => someSignal.value);
// Browser-only DOM manipulation
});When to Use useVisibleTask$:
Quality rules integrate with the plugin's documentation system:
// Example definitions for documentation
const preferClasslistExamples: QwikEslintExamples;
const loaderLocationExamples: QwikEslintExamples;
interface QwikEslintExamples {
[scenario: string]: {
good?: QwikEslintExample[];
bad?: QwikEslintExample[];
};
}Rules work together to enforce consistent code quality:
// Recommended configuration balances performance and usability
{
'qwik/prefer-classlist': 'warn', // Suggest native patterns
'qwik/loader-location': 'warn', // Ensure proper placement
'qwik/no-use-visible-task': 'warn', // Encourage alternatives
}
// Strict configuration enforces best practices
{
'qwik/prefer-classlist': 'error', // Require native patterns
'qwik/loader-location': 'error', // Enforce proper placement
'qwik/no-use-visible-task': 'warn', // Still warn (sometimes needed)
}Classlist Rule Benefits:
Loader Location Benefits:
Visible Task Alternatives:
useTask$ runs during SSR and hydration (better UX)useResource$ provides better caching and loading statesuseVisibleTask$ should be reserved for specific visibility-based needs// Quality rule definitions (standardized to TSESLint)
type Rule = TSESLint.RuleModule<any, any>;
// Classlist prop type
type ClassList =
| string
| string[]
| { [className: string]: boolean }
| (string | { [className: string]: boolean })[];
// Loader types
type RouteLoader<T> = () => Promise<T>;
type LoaderSignal<T> = {
readonly value: T;
};
// Task types
type TaskFn = (context: { track: <T>(signal: () => T) => T }) => void | Promise<void>;
type VisibleTaskFn = (context: { track: <T>(signal: () => T) => T }) => void | Promise<void>;
// Example system integration
interface QwikEslintExample {
code: string;
codeTitle?: string;
codeHighlight?: string;
description?: string;
}
interface QwikEslintExamples {
[scenario: string]: {
good?: QwikEslintExample[];
bad?: QwikEslintExample[];
};
}