Code style
Grafana’s codebase has been developed over time with a mix of styles. This guide explains how we write code going forward.
Frontend style guide
Grafana Labs follows the Airbnb React/JSX Style Guide.
Basic rules
- Keep files small and focused
- Break large components into sub-components
- Use spaces for indentation
Naming conventions
Class names
Use PascalCase:
// Good
class DataLink { }
// Bad
class dataLink { }
Constants
Use ALL_CAPS:
// Good
const CONSTANT_VALUE = "This string won't change";
// Bad
const constantValue = "This string won't change";
Functions and variables
Use camelCase:
// Good
const calculatePercentage = () => { }
const queryTargets = [];
// Bad
const CalculatePercentage = () => { }
const QueryTargets = [];
Interfaces and types
Use PascalCase (no I prefix):
// Good
interface ButtonProps { }
type RequestInfo = ...
// Bad
interface IButtonProps { }
interface button_props { }
Files and directories
-
Files: Name according to primary export
- PascalCase for classes/React components
- camelCase for functions
- Use
constants.ts for constants
- Use
actions.ts for Redux actions
- Use
reducers.ts for Redux reducers
- Use
*.test.ts(x) for test files
-
Directories: Use dash-case (kebab-case)
- Example:
features/new-important-feature/utils.ts
React components
Use function declarations:
// Good
export function Component(props: Props) { ... }
// Bad
export const Component = (props: Props) => { ... }
export const Component: React.FC<Props> = (props) => { ... }
Callback props should use on prefix:
// Good
onChange = () => { };
render() {
return <MyComponent onChange={this.onChange} />;
}
// Bad
handleChange = () => { };
render() {
return <MyComponent changed={this.handleChange} />;
}
Code organization
features/my-feature/
├── components/ # React components
├── state/ # Redux state and domain logic
│ ├── actions.ts
│ └── reducers.ts
├── api.ts # API calls (non-Redux thunk)
├── DashboardPage.tsx # Container/page component
└── MyFeature.test.tsx # Tests next to subject
For external plugin code:
- Components and types →
@grafana/ui
- Data models and utilities →
@grafana/data
- Runtime service interfaces →
@grafana/runtime
Exports
- Use named exports (not default exports)
- Use declaration exports:
export const foo = ...
- Only export code meant for external use
Type annotations
Let TypeScript infer types when possible, but:
// Good - explicitly type arrays
const stringArray: string[] = [];
// Good - specify function return types
function transform(value?: string): TransformedValue | undefined {
if (!value) return undefined;
return applyTransform(value);
}
// Bad - type inference fails
const stringArray = [];
State management
- Don’t mutate state in reducers or thunks
- Use
createSlice from Redux Toolkit
- Use
reducerTester to test reducers
- Use state selectors instead of accessing state directly
Styling
Use Emotion with useStyles2 hook:
const getStyles = (theme: GrafanaTheme2) => ({
elementWrapper: css({
padding: theme.spacing(1, 2),
background: theme.colors.background.secondary,
}),
});
// In component
const styles = useStyles2(getStyles);
SASS styles are deprecated. Migrate to Emotion when modifying SASS styles.
- Use TSDoc for documentation comments
- Use
/** ... */ for React prop documentation (react-docgen)
- Use inline comments inside functions and classes
Backend style guide
Follow these standard Go guidelines:
We use GolangCI-Lint with a custom configuration.
Run the linter:
Globals
Avoid global variables when possible. They make code difficult to maintain, reason about, and test. The Grafana codebase currently uses global variables (especially for configuration), but we’re working to reduce this.
Pointers
Prefer value types. Use pointers only when necessary:
- Passing modifiable arguments to functions
- Performance considerations (benchmark first!)
- When
nil has semantic meaning (prefer zero values when possible)
Pointers increase the risk of nil pointer panics.
Database patterns
Foreign keys
We generally don’t use foreign key constraints for historical and technical reasons.
Unique columns
If a column or column combination should be unique, add a uniqueness constraint through a migration.
XORM Session methods
Session.Insert() and Session.InsertOne() return the number of affected rows, NOT the newly introduced primary key. Contributors should be extra cautious when using them.
JSON
The simplejson package is legacy code. For new code, use the standard library encoding/json instead.
Frontend (ESLint)
We use @grafana/eslint-config with bulk suppressions.
yarn lint # Run ESLint
yarn lint:fix # Auto-fix issues
yarn lint:prune # Update suppressions file
ESLint runs:
- As a precommit hook (may auto-update
eslint-suppressions.json)
- In CI (must pass before merge)
Frontend (Prettier)
yarn prettier:check # Check formatting
yarn prettier:write # Auto-format files
Frontend (TypeScript)
yarn typecheck # Type check all code
Backend (Go)
make lint-go # Run GolangCI-Lint