Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 3 additions & 4 deletions docs.json
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,8 @@
"image-embeds",
"list-table",
"code",
"reusable-snippets"
"reusable-snippets",
"react-hooks"
]
},
{
Expand Down Expand Up @@ -203,9 +204,7 @@
},
{
"group": "Custom Components",
"pages": [
"content/components/reusable-components"
]
"pages": ["content/components/reusable-components"]
},
{
"group": "API Components",
Expand Down
212 changes: 212 additions & 0 deletions react-hooks.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,212 @@
---
title: "React Hooks"
description: "Learn how to use React hooks"
icon: "react"
---

import { Counter } from "/snippets/counter.mdx";
import { ColorGenerator } from "/snippets/color-generator.mdx";

Create interactive examples and dynamic content in your documentation using familiar [React hooks](https://react.dev/reference/react/hooks).

<Note>
No imports needed! All standard React hooks are automatically available in your MDX files.
</Note>

## Available Hooks

You can use all standard React hooks in your documentation, such as `useState`, `useEffect`, and other [React hooks](https://react.dev/reference/react/hooks).

## Interactive Examples

### 1. Creating Components Directly in MDX

Create a component directly in your MDX file using React hooks to build interactive elements that respond to user actions:

*A basic counter component created with `useState` hook*

<Counter />

```jsx
export const Counter = () => {
const [count, setCount] = useState(0);

return (
<div>
<p>Current count: {count}</p>
<button onClick={() => setCount(count + 1)}>
+
</button>
</div>
);
}

<Counter />
```


### 2. Importing Components from Snippet Files

Import snippets into your MDX files to create reusable interactive components. Store snippets in the `snippets` folder for better organization. Learn more in the [reusable snippets documentation](/reusable-snippets).

*Interactive HSL color generator created with multiple React hooks*


<ColorGenerator />

<Steps>
<Step title="Create a snippet file">
Create a new file in the `snippets` folder with your component code.

```jsx /snippets/color-generator.mdx [expandable]
export const ColorGenerator = () => {
const [hue, setHue] = useState(180)
const [saturation, setSaturation] = useState(50)
const [lightness, setLightness] = useState(50)
const [colors, setColors] = useState([])

useEffect(() => {
const newColors = []
for (let i = 0; i < 5; i++) {
const l = Math.max(10, Math.min(90, lightness - 20 + i * 10))
newColors.push(`hsl(${hue}, ${saturation}%, ${l}%)`)
}
setColors(newColors)
}, [hue, saturation, lightness])

const copyToClipboard = (color) => {
navigator.clipboard
.writeText(color)
.then(() => {
console.log(`Copied ${color} to clipboard!`)
})
.catch((err) => {
console.error("Failed to copy: ", err)
})
}

return (
<div className="p-4 border dark:border-zinc-800 rounded-xl bg-white dark:bg-gray-800 shadow-sm">
<span className="text-xl mb-4 font-semibold text-gray-800 dark:text-white">HSL Color Generator</span>

<div className="space-y-4">
<div className="space-y-2">
<label className="block text-sm text-gray-700 dark:text-gray-300">
Hue: {hue}°
<input
type="range"
min="0"
max="360"
value={hue}
onChange={(e) => setHue(Number.parseInt(e.target.value))}
className="w-full h-2 bg-gray-200 rounded-lg appearance-none cursor-pointer dark:bg-gray-700 mt-1"
style={{
background: `linear-gradient(to right,
hsl(0, ${saturation}%, ${lightness}%),
hsl(60, ${saturation}%, ${lightness}%),
hsl(120, ${saturation}%, ${lightness}%),
hsl(180, ${saturation}%, ${lightness}%),
hsl(240, ${saturation}%, ${lightness}%),
hsl(300, ${saturation}%, ${lightness}%),
hsl(360, ${saturation}%, ${lightness}%))`,
}}
/>
</label>

<label className="block text-sm text-gray-700 dark:text-gray-300">
Saturation: {saturation}%
<input
type="range"
min="0"
max="100"
value={saturation}
onChange={(e) => setSaturation(Number.parseInt(e.target.value))}
className="w-full h-2 bg-gray-200 rounded-lg appearance-none cursor-pointer dark:bg-gray-700 mt-1"
style={{
background: `linear-gradient(to right,
hsl(${hue}, 0%, ${lightness}%),
hsl(${hue}, 50%, ${lightness}%),
hsl(${hue}, 100%, ${lightness}%))`,
}}
/>
</label>

<label className="block text-sm text-gray-700 dark:text-gray-300">
Lightness: {lightness}%
<input
type="range"
min="0"
max="100"
value={lightness}
onChange={(e) => setLightness(Number.parseInt(e.target.value))}
className="w-full h-2 bg-gray-200 rounded-lg appearance-none cursor-pointer dark:bg-gray-700 mt-1"
style={{
background: `linear-gradient(to right,
hsl(${hue}, ${saturation}%, 0%),
hsl(${hue}, ${saturation}%, 50%),
hsl(${hue}, ${saturation}%, 100%))`,
}}
/>
</label>
</div>

<div className="flex space-x-1">
{colors.map((color, idx) => (
<div
key={idx}
className="h-16 rounded flex-1 cursor-pointer transition-transform hover:scale-105"
style={{ backgroundColor: color }}
title={`Click to copy: ${color}`}
onClick={() => copyToClipboard(color)}
/>
))}
</div>

<div className="text-sm font-mono text-gray-700 dark:text-gray-300">
<p>
Base color: hsl({hue}, {saturation}%, {lightness}%)
</p>
</div>
</div>
</div>
)
}
```
</Step>
<Step title="Import the snippet">
Add an import statement at the top of your MDX file:

```jsx
import { ColorGenerator } from "/snippets/color-generator.mdx"
```
</Step>
<Step title="Use the component">
<p>Add the component to your MDX content wherever needed:</p>

```jsx
<ColorGenerator />
```
</Step>
</Steps>

## Important Considerations

<AccordionGroup>
<Accordion title="Client-Side Rendering Impact">
React hook components render on the client-side, which has several implications:

- **SEO**: Search engines might not fully index dynamic content
- **Initial Load**: Visitors may experience a flash of loading content before components render
- **Accessibility**: Ensure dynamic content changes are announced to screen readers
</Accordion>

<Accordion title="Performance Best Practices">
- **Optimize Dependency Arrays**: Include only necessary dependencies in your `useEffect` dependency arrays
- **Memoize Complex Calculations**: Use `useMemo` or `useCallback` for expensive operations
- **Reduce Re-renders**: Break large components into smaller ones to prevent cascading re-renders
- **Lazy Loading**: Consider lazy loading complex components to improve initial page load time
</Accordion>
</AccordionGroup>



112 changes: 112 additions & 0 deletions snippets/color-generator.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
export const ColorGenerator = () => {
const [hue, setHue] = useState(165)

Check warning on line 2 in snippets/color-generator.mdx

View check run for this annotation

Mintlify / Mintlify Validation - vale-spellcheck

snippets/color-generator.mdx#L2

Did you really mean 'setHue'?
const [saturation, setSaturation] = useState(84)

Check warning on line 3 in snippets/color-generator.mdx

View check run for this annotation

Mintlify / Mintlify Validation - vale-spellcheck

snippets/color-generator.mdx#L3

Did you really mean 'setSaturation'?
const [lightness, setLightness] = useState(31)

Check warning on line 4 in snippets/color-generator.mdx

View check run for this annotation

Mintlify / Mintlify Validation - vale-spellcheck

snippets/color-generator.mdx#L4

Did you really mean 'setLightness'?
const [colors, setColors] = useState([])

Check warning on line 5 in snippets/color-generator.mdx

View check run for this annotation

Mintlify / Mintlify Validation - vale-spellcheck

snippets/color-generator.mdx#L5

Did you really mean 'setColors'?

useEffect(() => {
const newColors = []

Check warning on line 8 in snippets/color-generator.mdx

View check run for this annotation

Mintlify / Mintlify Validation - vale-spellcheck

snippets/color-generator.mdx#L8

Did you really mean 'newColors'?
for (let i = 0; i < 5; i++) {
const l = Math.max(10, Math.min(90, lightness - 20 + i * 10))
newColors.push(`hsl(${hue}, ${saturation}%, ${l}%)`)
}
setColors(newColors)
}, [hue, saturation, lightness])

const copyToClipboard = (color) => {
navigator.clipboard
.writeText(color)
.then(() => {
console.log(`Copied ${color} to clipboard!`)
})
.catch((err) => {
console.error("Failed to copy: ", err)
})
}

return (
<div className="p-4 border dark:border-zinc-800 rounded-xl bg-white dark:bg-gray-900/50 shadow-sm not-prose">
<p className="text-xl mb-4 font-semibold text-gray-800 dark:text-white">HSL Color Generator</p>

<div className="space-y-4">
<div className="space-y-2">
<label className="block text-sm text-gray-700 dark:text-gray-300">
Hue: {hue}°
<input
type="range"
min="0"
max="360"
value={hue}
onChange={(e) => setHue(Number.parseInt(e.target.value))}
className="w-full h-2 bg-gray-200 rounded-lg appearance-none cursor-pointer dark:bg-gray-700 mt-1"
style={{
background: `linear-gradient(to right,
hsl(0, ${saturation}%, ${lightness}%),
hsl(60, ${saturation}%, ${lightness}%),
hsl(120, ${saturation}%, ${lightness}%),
hsl(180, ${saturation}%, ${lightness}%),
hsl(240, ${saturation}%, ${lightness}%),
hsl(300, ${saturation}%, ${lightness}%),
hsl(360, ${saturation}%, ${lightness}%))`,
}}
/>
</label>

<label className="block text-sm text-gray-700 dark:text-gray-300">
Saturation: {saturation}%
<input
type="range"
min="0"
max="100"
value={saturation}
onChange={(e) => setSaturation(Number.parseInt(e.target.value))}
className="w-full h-2 bg-gray-200 rounded-lg appearance-none cursor-pointer dark:bg-gray-700 mt-1"
style={{
background: `linear-gradient(to right,
hsl(${hue}, 0%, ${lightness}%),
hsl(${hue}, 50%, ${lightness}%),
hsl(${hue}, 100%, ${lightness}%))`,
}}
/>
</label>

<label className="block text-sm text-gray-700 dark:text-gray-300">
Lightness: {lightness}%
<input
type="range"
min="0"
max="100"
value={lightness}
onChange={(e) => setLightness(Number.parseInt(e.target.value))}
className="w-full h-2 bg-gray-200 rounded-lg appearance-none cursor-pointer dark:bg-gray-700 mt-1"
style={{
background: `linear-gradient(to right,
hsl(${hue}, ${saturation}%, 0%),
hsl(${hue}, ${saturation}%, 50%),
hsl(${hue}, ${saturation}%, 100%))`,
}}
/>
</label>
</div>

<div className="flex space-x-2">
{colors.map((color, idx) => (
<div
key={idx}
className="h-16 rounded flex-1 cursor-pointer transition-transform hover:scale-105"
style={{ backgroundColor: color }}
title={`Click to copy: ${color}`}
onClick={() => copyToClipboard(color)}
/>
))}
</div>

<div className="text-sm font-mono text-gray-700 dark:text-gray-300">
<p>
Base color: hsl({hue}, {saturation}%, {lightness}%)
</p>
</div>
</div>
</div>
)
}
32 changes: 32 additions & 0 deletions snippets/counter.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
export const Counter = () => {
const [count, setCount] = useState(0)

Check warning on line 2 in snippets/counter.mdx

View check run for this annotation

Mintlify / Mintlify Validation - vale-spellcheck

snippets/counter.mdx#L2

Did you really mean 'setCount'?

const increment = () => setCount(count + 1)
const decrement = () => setCount(count - 1)

return (
<div className="flex items-center justify-center">
<div className="flex items-center rounded-lg overflow-hidden border border-gray-200 dark:border-gray-700 shadow-sm">
<button
onClick={decrement}
className="flex items-center justify-center h-8 w-8 bg-white hover:bg-gray-100 text-gray-800 dark:bg-gray-900 dark:hover:bg-gray-800 dark:text-gray-200 border-r border-gray-200 dark:border-gray-700"
aria-label="Decrease"
>
-
</button>

<div className="flex text-sm items-center justify-center h-8 px-6 bg-white dark:bg-gray-900 text-gray-900 dark:text-gray-100 font-medium min-w-[4rem] text-center">
{count}
</div>

<button
onClick={increment}
className="flex items-center justify-center h-8 w-8 bg-white hover:bg-gray-100 text-gray-800 dark:bg-gray-900 dark:hover:bg-gray-800 dark:text-gray-200 border-l border-gray-200 dark:border-gray-700"
aria-label="Increase"
>
+
</button>
</div>
</div>
)
}