Coding guidelines
Best practices we follow when writing Ariakit code examples for documentation purposes.
Overview
This document provides guidelines for writing code examples in Ariakit documentation. It serves as a reference for our preferred coding style, but it is not a general guide for writing code in your own app.
Feel free to consult this document for insights into our approach to code writing.
This is a living document, and certain code examples on the site might not be current. If you find any outdated examples, please submit a pull request to have them updated.
Prefer interface
over type
When using TypeScript to define prop types for components, we use interface
instead of type
. This is because interface
syntax supports extends
, which allows us to properly extend other types.
If you use the type
keyword with intersection (&
), it may silently introduce unintended or invalid types, which would be immediately detected if you used interface
instead:
// ❌ Bad, produces an invalid type without error
type CheckboxProps = React.ComponentPropsWithoutRef<"input"> & {
onChange?: (value: boolean) => void;
}
// ❗ Interface immediately detects the error
interface CheckboxProps extends React.ComponentPropsWithoutRef<"input"> {
onChange?: (value: boolean) => void;
}
// ✅ Good, fixed
interface CheckboxProps
extends Omit<React.ComponentPropsWithoutRef<"input">, "onChange"> {
onChange?: (value: boolean) => void;
}
JSDoc is also better merged with interface
. For example, tags such as @default
will be appropriately overridden, whereas type
would duplicate them.
In summary, according to the TypeScript documentation, it is recommended that you use interface
until you need to use features from type
.
Name functions inside forwardRef
When wrapping components with React.forwardRef
, we pass a named function as an argument instead of an anonymous arrow function. This is because React DevTools uses the function name to determine the component's name, eliminating the need to set displayName
:
// ❌ Bad
export const Combobox = React.forwardRef<HTMLInputElement, Props>(
(props, ref) => {
// ...
}
);
// ❌ Bad
Combobox.displayName = "Combobox";
// ✅ Good
export const Combobox = React.forwardRef<HTMLInputElement, Props>(
function Combobox(props, ref) {
// ...
}
);
Import namespace
When importing components from Ariakit and the import statement expands to multiple lines, we use the namespace import syntax instead. This improves code readability and makes it more concise.
// ❌ Bad (for documentation, but fine for app code)
import {
} from "@ariakit/react";
// ✅ Good
import * as Ariakit from "@ariakit/react";
// ✅ Good
This also simplifies the creation of abstractions for Ariakit components. For instance, when developing a custom Combobox
component, there's no need to rename the Ariakit Combobox
to a different name.
However, this isn't a strict rule for documentation. If it results in cleaner code overall, it makes more sense to import components individually, even if the import statement spans multiple lines.