|
| 1 | +--- |
| 2 | +title: "React Hooks" |
| 3 | +description: "Learn how to use React hooks" |
| 4 | +icon: "react" |
| 5 | +--- |
| 6 | + |
| 7 | + |
| 8 | +import { Counter } from "/snippets/counter.mdx"; |
| 9 | +import { ColorGenerator } from "/snippets/color-generator.mdx"; |
| 10 | + |
| 11 | +Create interactive examples and dynamic content in your documentation using familiar [React hooks](https://react.dev/reference/react/hooks). |
| 12 | + |
| 13 | +<Note> |
| 14 | + No imports needed! All standard React hooks are automatically available in your MDX files. |
| 15 | +</Note> |
| 16 | + |
| 17 | +## Available Hooks |
| 18 | + |
| 19 | +You can use all standard [React hooks](https://react.dev/reference/react/hooks) in your documentation, such as `useState`, `useEffect`, etc. |
| 20 | + |
| 21 | +## Examples |
| 22 | + |
| 23 | +### 1. Directly in your MDX file |
| 24 | +create a component directly in your MDX file. Use `useState` to create interactive components that respond to user actions: |
| 25 | + |
| 26 | +<Counter /> |
| 27 | + |
| 28 | +```jsx file.mdx |
| 29 | +export const Counter = () => { |
| 30 | + const [count, setCount] = useState(0); |
| 31 | + |
| 32 | + return ( |
| 33 | + <div> |
| 34 | + <p>Current count: {count}</p> |
| 35 | + <button onClick={() => setCount(count + 1)}> |
| 36 | + + |
| 37 | + </button> |
| 38 | + </div> |
| 39 | + ); |
| 40 | +} |
| 41 | + |
| 42 | +<Counter /> |
| 43 | +``` |
| 44 | + |
| 45 | + |
| 46 | +### 2. Import from a snippet file |
| 47 | + |
| 48 | +Your snippets can be imported into your MDX files to create interactive components. Snippets should be stored in the `snippets` folder, check out the [snippets](/reusable-snippets) page for more information. |
| 49 | + |
| 50 | + |
| 51 | +Here's an example of a snippet file that creates a color generator component: |
| 52 | + |
| 53 | +<ColorGenerator /> |
| 54 | + |
| 55 | +<Steps> |
| 56 | + <Step title="Create a snippet file"> |
| 57 | + Create a snippet file in the `snippets` folder. |
| 58 | + |
| 59 | + ```jsx /snippets/color-generator.mdx [expandable] |
| 60 | + export const ColorGenerator = () => { |
| 61 | + const [hue, setHue] = useState(180) |
| 62 | + const [saturation, setSaturation] = useState(50) |
| 63 | + const [lightness, setLightness] = useState(50) |
| 64 | + const [colors, setColors] = useState([]) |
| 65 | + |
| 66 | + useEffect(() => { |
| 67 | + const newColors = [] |
| 68 | + for (let i = 0; i < 5; i++) { |
| 69 | + const l = Math.max(10, Math.min(90, lightness - 20 + i * 10)) |
| 70 | + newColors.push(`hsl(${hue}, ${saturation}%, ${l}%)`) |
| 71 | + } |
| 72 | + setColors(newColors) |
| 73 | + }, [hue, saturation, lightness]) |
| 74 | + |
| 75 | + const copyToClipboard = (color) => { |
| 76 | + navigator.clipboard |
| 77 | + .writeText(color) |
| 78 | + .then(() => { |
| 79 | + console.log(`Copied ${color} to clipboard!`) |
| 80 | + }) |
| 81 | + .catch((err) => { |
| 82 | + console.error("Failed to copy: ", err) |
| 83 | + }) |
| 84 | + } |
| 85 | + |
| 86 | + return ( |
| 87 | + <div className="p-4 border dark:border-zinc-800 rounded-xl bg-white dark:bg-gray-800 shadow-sm"> |
| 88 | + <span className="text-xl mb-4 font-semibold text-gray-800 dark:text-white">HSL Color Generator</span> |
| 89 | + |
| 90 | + <div className="space-y-4"> |
| 91 | + <div className="space-y-2"> |
| 92 | + <label className="block text-sm text-gray-700 dark:text-gray-300"> |
| 93 | + Hue: {hue}° |
| 94 | + <input |
| 95 | + type="range" |
| 96 | + min="0" |
| 97 | + max="360" |
| 98 | + value={hue} |
| 99 | + onChange={(e) => setHue(Number.parseInt(e.target.value))} |
| 100 | + className="w-full h-2 bg-gray-200 rounded-lg appearance-none cursor-pointer dark:bg-gray-700 mt-1" |
| 101 | + style={{ |
| 102 | + background: `linear-gradient(to right, |
| 103 | + hsl(0, ${saturation}%, ${lightness}%), |
| 104 | + hsl(60, ${saturation}%, ${lightness}%), |
| 105 | + hsl(120, ${saturation}%, ${lightness}%), |
| 106 | + hsl(180, ${saturation}%, ${lightness}%), |
| 107 | + hsl(240, ${saturation}%, ${lightness}%), |
| 108 | + hsl(300, ${saturation}%, ${lightness}%), |
| 109 | + hsl(360, ${saturation}%, ${lightness}%))`, |
| 110 | + }} |
| 111 | + /> |
| 112 | + </label> |
| 113 | + |
| 114 | + <label className="block text-sm text-gray-700 dark:text-gray-300"> |
| 115 | + Saturation: {saturation}% |
| 116 | + <input |
| 117 | + type="range" |
| 118 | + min="0" |
| 119 | + max="100" |
| 120 | + value={saturation} |
| 121 | + onChange={(e) => setSaturation(Number.parseInt(e.target.value))} |
| 122 | + className="w-full h-2 bg-gray-200 rounded-lg appearance-none cursor-pointer dark:bg-gray-700 mt-1" |
| 123 | + style={{ |
| 124 | + background: `linear-gradient(to right, |
| 125 | + hsl(${hue}, 0%, ${lightness}%), |
| 126 | + hsl(${hue}, 50%, ${lightness}%), |
| 127 | + hsl(${hue}, 100%, ${lightness}%))`, |
| 128 | + }} |
| 129 | + /> |
| 130 | + </label> |
| 131 | + |
| 132 | + <label className="block text-sm text-gray-700 dark:text-gray-300"> |
| 133 | + Lightness: {lightness}% |
| 134 | + <input |
| 135 | + type="range" |
| 136 | + min="0" |
| 137 | + max="100" |
| 138 | + value={lightness} |
| 139 | + onChange={(e) => setLightness(Number.parseInt(e.target.value))} |
| 140 | + className="w-full h-2 bg-gray-200 rounded-lg appearance-none cursor-pointer dark:bg-gray-700 mt-1" |
| 141 | + style={{ |
| 142 | + background: `linear-gradient(to right, |
| 143 | + hsl(${hue}, ${saturation}%, 0%), |
| 144 | + hsl(${hue}, ${saturation}%, 50%), |
| 145 | + hsl(${hue}, ${saturation}%, 100%))`, |
| 146 | + }} |
| 147 | + /> |
| 148 | + </label> |
| 149 | + </div> |
| 150 | + |
| 151 | + <div className="flex space-x-1"> |
| 152 | + {colors.map((color, idx) => ( |
| 153 | + <div |
| 154 | + key={idx} |
| 155 | + className="h-16 rounded flex-1 cursor-pointer transition-transform hover:scale-105" |
| 156 | + style={{ backgroundColor: color }} |
| 157 | + title={`Click to copy: ${color}`} |
| 158 | + onClick={() => copyToClipboard(color)} |
| 159 | + /> |
| 160 | + ))} |
| 161 | + </div> |
| 162 | + |
| 163 | + <div className="text-sm font-mono text-gray-700 dark:text-gray-300"> |
| 164 | + <p> |
| 165 | + Base color: hsl({hue}, {saturation}%, {lightness}%) |
| 166 | + </p> |
| 167 | + </div> |
| 168 | + </div> |
| 169 | + </div> |
| 170 | + ) |
| 171 | + } |
| 172 | + |
| 173 | + ``` |
| 174 | + </Step> |
| 175 | + <Step title="Import the snippet"> |
| 176 | + On the top of your MDX file, import the snippet: |
| 177 | + |
| 178 | + ```jsx file.mdx |
| 179 | + import { ColorGenerator } from "/snippets/color-generator.mdx" |
| 180 | + ``` |
| 181 | + </Step> |
| 182 | + <Step title="Use the component"> |
| 183 | + <p>Use the component in your MDX file.</p> |
| 184 | + |
| 185 | + ```jsx file.mdx |
| 186 | + <ColorGenerator /> |
| 187 | + ``` |
| 188 | + </Step> |
| 189 | +</Steps> |
| 190 | + |
| 191 | + |
| 192 | +## Important Considerations |
| 193 | + |
| 194 | +<AccordionGroup> |
| 195 | + <Accordion title="Client-Side Rendering Impact"> |
| 196 | + React hook components are client-side rendered, which has several implications: |
| 197 | + |
| 198 | + - **SEO**: Search engines might not index dynamic content properly |
| 199 | + - **Initial Load**: Users might see a flash of loading content before the components render |
| 200 | + </Accordion> |
| 201 | + |
| 202 | + <Accordion title="Performance Best Practices"> |
| 203 | + - **Optimize Dependency Arrays**: Include only necessary dependencies in your `useEffect` dependency arrays |
| 204 | + - **Avoid Complex Calculations**: Move expensive operations to `useMemo` or `useCallback` |
| 205 | + - **Control Re-renders**: Break large components into smaller ones to prevent unnecessary re-renders |
| 206 | + </Accordion> |
| 207 | +</AccordionGroup> |
| 208 | + |
| 209 | + |
| 210 | + |
0 commit comments