Documenting Components

React Showroom generates documentations for your components based on type definition of your components, the comments on the type definitions, and markdown file.

Documenting Component Props

Declare your component props as usual in TypeScript:

src/components/button.tsx
import * as React from 'react';
export interface ButtonProps extends React.ComponentPropsWithoutRef<'button'> {
variant: 'primary' | 'secondary';
fullWidth?: boolean;
rounded?: 'sm' | 'lg';
}
export const Button = ({
type = 'button',
variant,
fullWidth,
rounded,
style = {},
...props
}: ButtonProps) => {
return (
<button
{...props}
style={{
...style,
display: 'inline-flex',
gap: 8,
alignItems: 'center',
backgroundColor: variant === 'primary' ? 'red' : 'blue',
color: '#efefef',
padding: '8px 16px',
width: fullWidth ? '100%' : undefined,
borderRadius: rounded ? (rounded === 'lg' ? 16 : 4) : undefined,
}}
type={type}
/>
);
};
tsx

And the following will be generated:

http://localhost:6969

Button

NAMETYPEDESCRIPTION
variant"primary" | "secondary"
fullWidthboolean
rounded"sm" | "lg"

Comments for Component and Props

To provide more descriptions to the props and the comment itself, you can add JSDoc style comments.

src/components/button.tsx
import * as React from 'react';
export interface ButtonProps extends React.ComponentPropsWithoutRef<'button'> {
/**
* variant for the button
*/
variant: 'primary' | 'secondary';
/**
* specify if the button should takes up all the available horizontal space.
*/
fullWidth?: boolean;
/**
* controls the border radius.
*/
rounded?: 'sm' | 'lg';
}
/**
* `<Button />` component is a wrapper of `<button>` element.
*
* Unspecified props will be spreaded.
*/
export const ButtonWithComments = ({
type = 'button',
variant,
fullWidth,
rounded,
...props
}: ButtonProps) => {
return (
<button
{...props}
style={{
backgroundColor: variant === 'primary' ? 'red' : 'blue',
color: '#efefef',
padding: '8px 16px',
width: fullWidth ? '100%' : undefined,
borderRadius: rounded ? (rounded === 'lg' ? 16 : 4) : undefined,
}}
type={type}
/>
);
};
tsx

And the documentations will be reflected.

http://localhost:6969

ButtonWithComments

<Button /> component is a wrapper of <button> element.
Unspecified props will be spreaded.
NAMETYPEDESCRIPTION
variant"primary" | "secondary"variant for the button
fullWidthbooleanspecify if the button should takes up all the available horizontal space.
rounded"sm" | "lg"controls the border radius.

JSDoc tags will be parsed and displayed as well.

src/components/button.tsx
import * as React from 'react';
export interface OldButtonProps
extends React.ComponentPropsWithoutRef<'button'> {
color: 'primary' | 'secondary';
}
/**
* @deprecated use `<Button />` instead.
*
* @version 1.0.0
* @see http://some-stackoverflow-question-that-i-copy-but-dont-understand.com
*/
export const OldButton = ({
type = 'button',
color,
...props
}: OldButtonProps) => {
return <button {...props} type={type} />;
};
tsx

will results in

http://localhost:6969

OldButton

deprecated: use `<Button />` instead.

version: 1.0.0

see: http://some-stackoverflow-question-that-i-copy-but-dont-understand.com

NAMETYPEDESCRIPTION
color"primary" | "secondary"

Show Usages with Markdown

To show usage examples for the component, create a .mdx file next to the component with the same name as the component file.

src/components/button.mdx
```tsx
<Button>Hello</Button>
```
## Using React Hooks
Example with states:
```tsx
import * as React from 'react';
const Example = () => {
const [on, setOn] = React.useState<boolean>(false);
return <Button onClick={() => setOn(!on)}>{on ? 'On' : 'Off'}</Button>;
};
<Example />;
```
Add `frame` to render examples in iframe. This is very handy to see how the component is rendered in different screen size.
```tsx frame
<Button>Hello</Button>
```
Calling `console.log`, `console.info`, `console.warn`, `console.error` in the example will log the result in a separate panel.
```tsx frame
import * as React from 'react';
const Example = () => {
const [on, setOn] = React.useState<boolean>(false);
return (
<Button
onClick={() => {
console.log(`Changing`, { from: on, to: !on });
setOn(!on);
}}
>
{on ? 'On' : 'Off'}
</Button>
);
};
<Example />;
```
mdx

will produces the following output.

http://localhost:6969/
In this page

Button

NAMETYPEDESCRIPTION
variant"primary" | "secondary"
fullWidthboolean
rounded"sm" | "lg"

Using React Hooks

Example with states:

Add frame to render examples in iframe. This is very handy to see how the component is rendered in different screen size.

Calling console.log, console.info, console.warn, console.error in the example will log the result in a separate panel.

Open the hamburget menu and choose view as Designer to see the technical details hidden.

This is handy for non-technical audience (designer/business) to view the site.

Few things to take note:

  • You must tag the code block with tsx or jsx for it to become live example.
  • If the last expression in your code block is a JSX, that will be the displayed result of the example.
  • The component is automatically injected into the scope, so you doesn't need to import it.
  • React is automatically injected into the scope as well, but it's fine if you want to import it.

Handy features for collaborations:

  • If you click the "Standalone" button to open the standalone view, a page with only the particular example will be displayed. This allows you to share the URL to others and they can open the specific example using the URL.
  • If you edit the code example in standalone view, the change will be encoded in the URL as well. You can use that to tweak the example and share the changes.

A few scenarios for code blocks:

src/components/button.mdx
Manually call `render` if you want to add some operations before rendering the output.
```tsx
const wait = (ms: number) => new Promise((fulfill) => setTimeout(fulfill, ms));
wait(1000).then(() => render(<Button>Delayed</Button>));
```
Importing third party libraries work as well as long as they are installed in your project.
If user add additional import statement in live editor, they will be loaded from [Skypack](https://www.skypack.dev/) on the fly.
```tsx
import { useForm } from 'react-hook-form';
const FormExample = () => {
const {
register,
handleSubmit,
watch,
formState: { errors },
} = useForm();
const onSubmit = (data) => console.log(data);
return (
<form onSubmit={handleSubmit(onSubmit)} className="space-y-5">
<div className="space-x-2">
<label htmlFor="firstName">First Name</label>
<input type="text" defaultValue="John" {...register('firstName')} />
</div>
<div className="space-x-2">
<label htmlFor="lastName">Last Name</label>
<input type="text" {...register('lastName', { required: true })} />
{errors.lastName && <span>This field is required</span>}
</div>
<Button type="submit">Submit</Button>
</form>
);
};
<FormExample />;
```
Use `fileName` modifier to add a file name at the top of the code block.
```tsx fileName="code.tsx"
import * as React from 'react';
const Example = () => {
const [on, setOn] = React.useState<boolean>(false);
return <Button onClick={() => setOn(!on)}>{on ? 'On' : 'Off'}</Button>;
};
<Example />;
```
For code blocks with `frame`, by default we will adjust the given height based on the rendered content.
You can overwrite the height (fixed) and initial height (initial iframe height while waiting the content to be rendered) using `height` and `initialHeight` modifier.
```tsx frame height=100
<Button>Height: 100</Button>
```
```tsx frame initialHeight=48
<Button>Initial Height: 48</Button>
```
Use `noEditor` modifier to show only the output result.
```tsx noEditor
import * as React from 'react';
const Example = () => {
const [on, setOn] = React.useState<boolean>(false);
return <Button onClick={() => setOn(!on)}>{on ? 'On' : 'Off'}</Button>;
};
<Example />;
```
Use `static` modifier to show static code.
```tsx static
<Button>Hello</Button>
```
For static code blocks, you can use `highlights` to highlight specific lines.
```tsx static highlights={3-7}
import * as React from 'react';
const Example = () => {
const [on, setOn] = React.useState<boolean>(false);
return <Button onClick={() => setOn(!on)}>{on ? 'On' : 'Off'}</Button>;
};
<Example />;
```
mdx

And the result:

http://localhost:6969/

Button

NAMETYPEDESCRIPTION
variant"primary" | "secondary"
fullWidthboolean
rounded"sm" | "lg"

Manually call render if you want to add some operations before rendering the output.

Importing third party libraries work as well as long as they are installed in your project.

If user add additional import statement in live editor, they will be loaded from Skypack on the fly.

Use fileName modifier to add a file name at the top of the code block.

code.tsx

For code blocks with frame, by default we will adjust the given height based on the rendered content.

You can overwrite the height (fixed) and initial height (initial iframe height while waiting the content to be rendered) using height and initialHeight modifier.

Use noEditor modifier to show only the output result.

Use static modifier to show static code.

<Button>Hello</Button>
tsx

For static code blocks, you can use highlights to highlight specific lines.

import * as React from 'react';
const Example = () => {
const [on, setOn] = React.useState<boolean>(false);
return <Button onClick={() => setOn(!on)}>{on ? 'On' : 'Off'}</Button>;
};
<Example />;
tsx

Show Different Variants

A common use case is to show different variants of the component side-by-side so they can be compared visually.

You can, of course, render the component by passing different props to it in an example to do that.

However, you need to maintain the example so any changes to the props will be reflected in the example.

Another options is to use the useUnionProps custom hook (available in react-showroom/client), which provide the variants (parsed from the props type definition) as options to your examples.

src/components/button.mdx
```tsx
import { useUnionProps } from 'react-showroom/client';
const Variants = () => {
const variantOptions = useUnionProps('variant');
const roundedOptions = useUnionProps('rounded');
return (
<div>
{variantOptions.map((vOption) => (
<div
style={{
display: 'flex',
gap: '1rem',
marginBottom: '0.5rem',
alignItems: 'center',
}}
key={vOption.value}
>
<div>{vOption.label}</div>
{roundedOptions.map((rOption) => (
<Button
variant={vOption.value}
rounded={rOption.value}
key={rOption.value}
>
rounded="{rOption.label}"
</Button>
))}
</div>
))}
</div>
);
};
<Variants />;
```
mdx
http://localhost:6969/

Button

NAMETYPEDESCRIPTION
variant"primary" | "secondary"
fullWidthboolean
rounded"sm" | "lg"
primary
secondary