JSDoc
Documenting defaults, examples, and deprecations so consumers don't have to read your source.
Simple prop documentation
interface BadgeProps {
label: string;
variant: 'default' | 'success' | 'warning';
}interface BadgeProps {
/** The text displayed inside the badge. */
label: string;
/**
* The visual style variant of the badge.
* @default 'default'
*/
variant: 'default' | 'success' | 'warning';
}Even simple props benefit from JSDoc. When another developer hovers over variant in their IDE, they instantly see what it does and what the default is. The @default tag is especially useful for optional props.
Component documentation
interface TooltipProps {
/** The content. */
content: string;
/** The children. */
children: React.ReactNode;
/** The position. */
position?: 'top' | 'bottom';
/** The delay. */
delay?: number;
}interface TooltipProps {
/** The text content displayed inside the tooltip. */
content: string;
/** The element that triggers the tooltip on hover. */
children: React.ReactNode;
/**
* Where the tooltip appears relative to the trigger.
* @default 'top'
*/
position?: 'top' | 'bottom';
/**
* Delay in milliseconds before the tooltip appears.
* @default 200
*/
delay?: number;
}Good JSDoc explains what a prop does and documents defaults. "The text content displayed inside the tooltip" is useful; "The content" just repeats the prop name.
The @default tags on optional props are especially valuable — no need to read the implementation to know the fallback.
Callback prop documentation
interface DataTableProps<T> {
/** The data to display. */
data: T[];
/** Handler for row clicks. */
onRowClick?: (row: T) => void;
/** Handler for sorting. */
onSort?: (col: string) => void;
}interface DataTableProps<T> {
/** The array of data objects to render as rows. */
data: T[];
/**
* Called when a row is clicked.
* Receives the full data object for the clicked row.
*/
onRowClick?: (row: T) => void;
/**
* Called when a column header is clicked for sorting.
* Receives the column key that was clicked.
*/
onSort?: (columnKey: string) => void;
}Good callback documentation explains when the callback fires and what the parameters represent. "Called when a column header is clicked for sorting" tells you the trigger. The rename from col to columnKey reinforces clarity. Compare "Handler for sorting" — that just restates the prop name.
Realistic JSDoc examples
/**
* A confirmation dialog component.
*
* @example
* <ConfirmDialog
* title="Title"
* message="Message"
* onConfirm={handleConfirm}
* onCancel={handleCancel}
* />
*/
interface ConfirmDialogProps {
/** The title. */
title: string;
/** The message. */
message: string;
/** Confirm handler. */
onConfirm: () => void;
/** Cancel handler. */
onCancel: () => void;
/** The variant. */
variant?: 'info' | 'danger';
}/**
* A modal dialog that asks the user to confirm
* or cancel a destructive action.
*
* @example
* <ConfirmDialog
* title="Delete project?"
* message="This cannot be undone."
* variant="danger"
* onConfirm={handleDelete}
* onCancel={handleDismiss}
* />
*/
interface ConfirmDialogProps {
/** The heading text of the dialog. */
title: string;
/** The body message explaining the action. */
message: string;
/** Called when the user clicks the confirm button. */
onConfirm: () => void;
/** Called when the user dismisses or cancels. */
onCancel: () => void;
/**
* Visual style of the dialog.
* Use `'danger'` for destructive actions.
* @default 'info'
*/
variant?: 'info' | 'danger';
}A good @example uses realistic prop values that show how the component is actually used. title="Delete project?" and variant="danger" paint a real scenario.
Prop-level JSDoc explains purpose, not just the name: "The heading text of the dialog" vs "The title."
Deprecation notice in JSDoc
interface AccordionProps {
// Don't use isOpen, use expanded instead
isOpen?: boolean;
expanded?: boolean;
children: React.ReactNode;
onToggle?: () => void;
}interface AccordionProps {
/**
* @deprecated Use `isExpanded` instead.
* Will be removed in v3.0.
*/
isOpen?: boolean;
/** Whether the section is expanded. */
isExpanded?: boolean;
children: React.ReactNode;
onToggle?: () => void;
}The @deprecated JSDoc tag triggers a visual strikethrough in most IDEs and shows a warning on hover. It's machine-readable — linters can flag usage. A code comment is invisible at the call site and can't be enforced by tooling.