Repository: reactjs/ko.react.dev
Branch: main
Commit: 9067b769996d
Files: 476
Total size: 4.8 MB
Directory structure:
gitextract_nwqdaqk2/
├── .claude/
│ ├── agents/
│ │ └── docs-reviewer.md
│ ├── settings.json
│ └── skills/
│ ├── docs-components/
│ │ └── SKILL.md
│ ├── docs-rsc-sandpack/
│ │ └── SKILL.md
│ ├── docs-sandpack/
│ │ └── SKILL.md
│ ├── docs-voice/
│ │ └── SKILL.md
│ ├── docs-writer-blog/
│ │ └── SKILL.md
│ ├── docs-writer-learn/
│ │ └── SKILL.md
│ ├── docs-writer-reference/
│ │ └── SKILL.md
│ ├── react-expert/
│ │ └── SKILL.md
│ ├── review-docs/
│ │ └── SKILL.md
│ └── write/
│ └── SKILL.md
├── .editorconfig
├── .eslintignore
├── .eslintrc
├── .gitattributes
├── .github/
│ ├── CODEOWNERS
│ ├── FUNDING.yml
│ ├── ISSUE_TEMPLATE/
│ │ ├── 0-bug.yml
│ │ ├── 1-typo.yml
│ │ ├── 3-framework.yml
│ │ ├── config.yml
│ │ ├── need-translation.md
│ │ └── term.md
│ ├── PULL_REQUEST_TEMPLATE.md
│ ├── dependabot.yml
│ └── workflows/
│ ├── analyze.yml
│ ├── analyze_comment.yml
│ ├── site_lint.yml
│ ├── textlint_lint.yml
│ └── textlint_test.yml
├── .gitignore
├── .husky/
│ ├── common.sh
│ └── pre-commit
├── .prettierignore
├── .prettierrc
├── .textlintrc.js
├── .vscode/
│ ├── extensions.json
│ └── settings.json
├── CLAUDE.md
├── CODE_OF_CONDUCT.md
├── CONTRIBUTING.md
├── LICENSE-DOCS.md
├── README.md
├── colors.js
├── eslint-local-rules/
│ ├── __tests__/
│ │ ├── fixtures/
│ │ │ └── src/
│ │ │ └── content/
│ │ │ ├── basic-error.md
│ │ │ ├── duplicate-metadata.md
│ │ │ ├── malformed-metadata.md
│ │ │ ├── mixed-language.md
│ │ │ ├── stale-expected-error.md
│ │ │ └── suppressed-error.md
│ │ └── lint-markdown-code-blocks.test.js
│ ├── index.js
│ ├── package.json
│ ├── parser.js
│ └── rules/
│ ├── diagnostics.js
│ ├── lint-markdown-code-blocks.js
│ ├── markdown.js
│ ├── metadata.js
│ └── react-compiler.js
├── eslint.config.mjs
├── lint-staged.config.js
├── next-env.d.ts
├── next.config.js
├── package.json
├── plugins/
│ ├── markdownToHtml.js
│ ├── remark-header-custom-ids.js
│ └── remark-smartypants.js
├── postcss.config.js
├── public/
│ ├── .well-known/
│ │ └── atproto-did
│ ├── browserconfig.xml
│ ├── html/
│ │ └── single-file-example.html
│ ├── js/
│ │ ├── jsfiddle-integration-babel.js
│ │ └── jsfiddle-integration.js
│ ├── robots.txt
│ └── site.webmanifest
├── scripts/
│ ├── buildRscWorker.mjs
│ ├── copyright.js
│ ├── deadLinkChecker.js
│ ├── downloadFonts.mjs
│ ├── generateRss.js
│ ├── headingIDHelpers/
│ │ ├── generateHeadingIDs.js
│ │ ├── validateHeadingIDs.js
│ │ └── walk.js
│ └── headingIdLinter.js
├── src/
│ ├── components/
│ │ ├── Breadcrumbs.tsx
│ │ ├── Button.tsx
│ │ ├── ButtonLink.tsx
│ │ ├── DocsFooter.tsx
│ │ ├── ErrorDecoderContext.tsx
│ │ ├── ExternalLink.tsx
│ │ ├── Icon/
│ │ │ ├── IconArrow.tsx
│ │ │ ├── IconArrowSmall.tsx
│ │ │ ├── IconBsky.tsx
│ │ │ ├── IconCanary.tsx
│ │ │ ├── IconChevron.tsx
│ │ │ ├── IconClose.tsx
│ │ │ ├── IconCodeBlock.tsx
│ │ │ ├── IconCopy.tsx
│ │ │ ├── IconDeepDive.tsx
│ │ │ ├── IconDownload.tsx
│ │ │ ├── IconError.tsx
│ │ │ ├── IconExperimental.tsx
│ │ │ ├── IconFacebookCircle.tsx
│ │ │ ├── IconGitHub.tsx
│ │ │ ├── IconHamburger.tsx
│ │ │ ├── IconHint.tsx
│ │ │ ├── IconInstagram.tsx
│ │ │ ├── IconLink.tsx
│ │ │ ├── IconNavArrow.tsx
│ │ │ ├── IconNewPage.tsx
│ │ │ ├── IconNote.tsx
│ │ │ ├── IconPitfall.tsx
│ │ │ ├── IconRestart.tsx
│ │ │ ├── IconRocket.tsx
│ │ │ ├── IconRss.tsx
│ │ │ ├── IconSearch.tsx
│ │ │ ├── IconSolution.tsx
│ │ │ ├── IconTerminal.tsx
│ │ │ ├── IconThreads.tsx
│ │ │ ├── IconTwitter.tsx
│ │ │ └── IconWarning.tsx
│ │ ├── Layout/
│ │ │ ├── Feedback.tsx
│ │ │ ├── Footer.tsx
│ │ │ ├── HomeContent.js
│ │ │ ├── Page.tsx
│ │ │ ├── Sidebar/
│ │ │ │ ├── SidebarButton.tsx
│ │ │ │ ├── SidebarLink.tsx
│ │ │ │ ├── SidebarRouteTree.tsx
│ │ │ │ └── index.tsx
│ │ │ ├── SidebarNav/
│ │ │ │ ├── SidebarNav.tsx
│ │ │ │ └── index.tsx
│ │ │ ├── Toc.tsx
│ │ │ ├── TopNav/
│ │ │ │ ├── BrandMenu.tsx
│ │ │ │ ├── TopNav.tsx
│ │ │ │ └── index.tsx
│ │ │ ├── getRouteMeta.tsx
│ │ │ └── useTocHighlight.tsx
│ │ ├── Logo.tsx
│ │ ├── MDX/
│ │ │ ├── BlogCard.tsx
│ │ │ ├── Challenges/
│ │ │ │ ├── Challenge.tsx
│ │ │ │ ├── Challenges.tsx
│ │ │ │ ├── Navigation.tsx
│ │ │ │ └── index.tsx
│ │ │ ├── CodeBlock/
│ │ │ │ ├── CodeBlock.tsx
│ │ │ │ └── index.tsx
│ │ │ ├── CodeDiagram.tsx
│ │ │ ├── ConsoleBlock.tsx
│ │ │ ├── Diagram.tsx
│ │ │ ├── DiagramGroup.tsx
│ │ │ ├── ErrorDecoder.tsx
│ │ │ ├── ExpandableCallout.tsx
│ │ │ ├── ExpandableExample.tsx
│ │ │ ├── Heading.tsx
│ │ │ ├── InlineCode.tsx
│ │ │ ├── Intro.tsx
│ │ │ ├── LanguagesContext.tsx
│ │ │ ├── Link.tsx
│ │ │ ├── MDXComponents.module.css
│ │ │ ├── MDXComponents.tsx
│ │ │ ├── PackageImport.tsx
│ │ │ ├── Recap.tsx
│ │ │ ├── Sandpack/
│ │ │ │ ├── ClearButton.tsx
│ │ │ │ ├── Console.tsx
│ │ │ │ ├── CustomPreset.tsx
│ │ │ │ ├── DownloadButton.tsx
│ │ │ │ ├── ErrorMessage.tsx
│ │ │ │ ├── LoadingOverlay.tsx
│ │ │ │ ├── NavigationBar.tsx
│ │ │ │ ├── OpenInCodeSandboxButton.tsx
│ │ │ │ ├── OpenInTypeScriptPlayground.tsx
│ │ │ │ ├── Preview.tsx
│ │ │ │ ├── ReloadButton.tsx
│ │ │ │ ├── ResetButton.tsx
│ │ │ │ ├── SandpackRSCRoot.tsx
│ │ │ │ ├── SandpackRoot.tsx
│ │ │ │ ├── Themes.tsx
│ │ │ │ ├── createFileMap.ts
│ │ │ │ ├── index.tsx
│ │ │ │ ├── runESLint.tsx
│ │ │ │ ├── sandpack-rsc/
│ │ │ │ │ ├── RscFileBridge.tsx
│ │ │ │ │ └── sandbox-code/
│ │ │ │ │ └── src/
│ │ │ │ │ ├── __react_refresh_init__.js
│ │ │ │ │ ├── rsc-client.js
│ │ │ │ │ ├── rsc-server.js
│ │ │ │ │ ├── webpack-shim.js
│ │ │ │ │ └── worker-bundle.dist.js
│ │ │ │ ├── template.ts
│ │ │ │ ├── templateRSC.ts
│ │ │ │ └── useSandpackLint.tsx
│ │ │ ├── SandpackWithHTMLOutput.tsx
│ │ │ ├── SimpleCallout.tsx
│ │ │ ├── TeamMember.tsx
│ │ │ ├── TerminalBlock.tsx
│ │ │ ├── TocContext.tsx
│ │ │ └── YouWillLearnCard.tsx
│ │ ├── PageHeading.tsx
│ │ ├── Search.tsx
│ │ ├── Seo.tsx
│ │ ├── SocialBanner.tsx
│ │ └── Tag.tsx
│ ├── content/
│ │ ├── blog/
│ │ │ ├── 2020/
│ │ │ │ └── 12/
│ │ │ │ └── 21/
│ │ │ │ └── data-fetching-with-react-server-components.md
│ │ │ ├── 2021/
│ │ │ │ ├── 06/
│ │ │ │ │ └── 08/
│ │ │ │ │ └── the-plan-for-react-18.md
│ │ │ │ └── 12/
│ │ │ │ └── 17/
│ │ │ │ └── react-conf-2021-recap.md
│ │ │ ├── 2022/
│ │ │ │ ├── 03/
│ │ │ │ │ ├── 08/
│ │ │ │ │ │ └── react-18-upgrade-guide.md
│ │ │ │ │ └── 29/
│ │ │ │ │ └── react-v18.md
│ │ │ │ └── 06/
│ │ │ │ └── 15/
│ │ │ │ └── react-labs-what-we-have-been-working-on-june-2022.md
│ │ │ ├── 2023/
│ │ │ │ ├── 03/
│ │ │ │ │ ├── 16/
│ │ │ │ │ │ └── introducing-react-dev.md
│ │ │ │ │ └── 22/
│ │ │ │ │ └── react-labs-what-we-have-been-working-on-march-2023.md
│ │ │ │ └── 05/
│ │ │ │ └── 03/
│ │ │ │ └── react-canaries.md
│ │ │ ├── 2024/
│ │ │ │ ├── 02/
│ │ │ │ │ └── 15/
│ │ │ │ │ └── react-labs-what-we-have-been-working-on-february-2024.md
│ │ │ │ ├── 04/
│ │ │ │ │ └── 25/
│ │ │ │ │ └── react-19-upgrade-guide.md
│ │ │ │ ├── 05/
│ │ │ │ │ └── 22/
│ │ │ │ │ └── react-conf-2024-recap.md
│ │ │ │ ├── 10/
│ │ │ │ │ └── 21/
│ │ │ │ │ └── react-compiler-beta-release.md
│ │ │ │ └── 12/
│ │ │ │ └── 05/
│ │ │ │ └── react-19.md
│ │ │ ├── 2025/
│ │ │ │ ├── 02/
│ │ │ │ │ └── 14/
│ │ │ │ │ └── sunsetting-create-react-app.md
│ │ │ │ ├── 04/
│ │ │ │ │ ├── 21/
│ │ │ │ │ │ └── react-compiler-rc.md
│ │ │ │ │ └── 23/
│ │ │ │ │ └── react-labs-view-transitions-activity-and-more.md
│ │ │ │ ├── 10/
│ │ │ │ │ ├── 01/
│ │ │ │ │ │ └── react-19-2.md
│ │ │ │ │ ├── 07/
│ │ │ │ │ │ ├── introducing-the-react-foundation.md
│ │ │ │ │ │ └── react-compiler-1.md
│ │ │ │ │ └── 16/
│ │ │ │ │ └── react-conf-2025-recap.md
│ │ │ │ └── 12/
│ │ │ │ ├── 03/
│ │ │ │ │ └── critical-security-vulnerability-in-react-server-components.md
│ │ │ │ └── 11/
│ │ │ │ └── denial-of-service-and-source-code-exposure-in-react-server-components.md
│ │ │ ├── 2026/
│ │ │ │ └── 02/
│ │ │ │ └── 24/
│ │ │ │ └── the-react-foundation.md
│ │ │ └── index.md
│ │ ├── community/
│ │ │ ├── acknowledgements.md
│ │ │ ├── conferences.md
│ │ │ ├── docs-contributors.md
│ │ │ ├── index.md
│ │ │ ├── meetups.md
│ │ │ ├── team.md
│ │ │ ├── translations.md
│ │ │ ├── versioning-policy.md
│ │ │ └── videos.md
│ │ ├── errors/
│ │ │ ├── 377.md
│ │ │ ├── generic.md
│ │ │ └── index.md
│ │ ├── index.md
│ │ ├── learn/
│ │ │ ├── add-react-to-an-existing-project.md
│ │ │ ├── adding-interactivity.md
│ │ │ ├── build-a-react-app-from-scratch.md
│ │ │ ├── choosing-the-state-structure.md
│ │ │ ├── conditional-rendering.md
│ │ │ ├── creating-a-react-app.md
│ │ │ ├── describing-the-ui.md
│ │ │ ├── editor-setup.md
│ │ │ ├── escape-hatches.md
│ │ │ ├── extracting-state-logic-into-a-reducer.md
│ │ │ ├── importing-and-exporting-components.md
│ │ │ ├── index.md
│ │ │ ├── installation.md
│ │ │ ├── javascript-in-jsx-with-curly-braces.md
│ │ │ ├── keeping-components-pure.md
│ │ │ ├── lifecycle-of-reactive-effects.md
│ │ │ ├── managing-state.md
│ │ │ ├── manipulating-the-dom-with-refs.md
│ │ │ ├── passing-data-deeply-with-context.md
│ │ │ ├── passing-props-to-a-component.md
│ │ │ ├── preserving-and-resetting-state.md
│ │ │ ├── queueing-a-series-of-state-updates.md
│ │ │ ├── react-compiler/
│ │ │ │ ├── debugging.md
│ │ │ │ ├── incremental-adoption.md
│ │ │ │ ├── index.md
│ │ │ │ ├── installation.md
│ │ │ │ └── introduction.md
│ │ │ ├── react-developer-tools.md
│ │ │ ├── reacting-to-input-with-state.md
│ │ │ ├── referencing-values-with-refs.md
│ │ │ ├── removing-effect-dependencies.md
│ │ │ ├── render-and-commit.md
│ │ │ ├── rendering-lists.md
│ │ │ ├── responding-to-events.md
│ │ │ ├── reusing-logic-with-custom-hooks.md
│ │ │ ├── rsc-sandbox-test.md
│ │ │ ├── scaling-up-with-reducer-and-context.md
│ │ │ ├── separating-events-from-effects.md
│ │ │ ├── setup.md
│ │ │ ├── sharing-state-between-components.md
│ │ │ ├── start-a-new-react-project.md
│ │ │ ├── state-a-components-memory.md
│ │ │ ├── state-as-a-snapshot.md
│ │ │ ├── synchronizing-with-effects.md
│ │ │ ├── thinking-in-react.md
│ │ │ ├── tutorial-tic-tac-toe.md
│ │ │ ├── typescript.md
│ │ │ ├── understanding-your-ui-as-a-tree.md
│ │ │ ├── updating-arrays-in-state.md
│ │ │ ├── updating-objects-in-state.md
│ │ │ ├── writing-markup-with-jsx.md
│ │ │ ├── you-might-not-need-an-effect.md
│ │ │ └── your-first-component.md
│ │ ├── reference/
│ │ │ ├── dev-tools/
│ │ │ │ └── react-performance-tracks.md
│ │ │ ├── eslint-plugin-react-hooks/
│ │ │ │ ├── index.md
│ │ │ │ └── lints/
│ │ │ │ ├── component-hook-factories.md
│ │ │ │ ├── config.md
│ │ │ │ ├── error-boundaries.md
│ │ │ │ ├── exhaustive-deps.md
│ │ │ │ ├── gating.md
│ │ │ │ ├── globals.md
│ │ │ │ ├── immutability.md
│ │ │ │ ├── incompatible-library.md
│ │ │ │ ├── preserve-manual-memoization.md
│ │ │ │ ├── purity.md
│ │ │ │ ├── refs.md
│ │ │ │ ├── rules-of-hooks.md
│ │ │ │ ├── set-state-in-effect.md
│ │ │ │ ├── set-state-in-render.md
│ │ │ │ ├── static-components.md
│ │ │ │ ├── unsupported-syntax.md
│ │ │ │ └── use-memo.md
│ │ │ ├── react/
│ │ │ │ ├── Activity.md
│ │ │ │ ├── Children.md
│ │ │ │ ├── Component.md
│ │ │ │ ├── Fragment.md
│ │ │ │ ├── Profiler.md
│ │ │ │ ├── PureComponent.md
│ │ │ │ ├── StrictMode.md
│ │ │ │ ├── Suspense.md
│ │ │ │ ├── ViewTransition.md
│ │ │ │ ├── act.md
│ │ │ │ ├── addTransitionType.md
│ │ │ │ ├── apis.md
│ │ │ │ ├── cache.md
│ │ │ │ ├── cacheSignal.md
│ │ │ │ ├── captureOwnerStack.md
│ │ │ │ ├── cloneElement.md
│ │ │ │ ├── components.md
│ │ │ │ ├── createContext.md
│ │ │ │ ├── createElement.md
│ │ │ │ ├── createFactory.md
│ │ │ │ ├── createRef.md
│ │ │ │ ├── experimental_taintObjectReference.md
│ │ │ │ ├── experimental_taintUniqueValue.md
│ │ │ │ ├── experimental_useEffectEvent.md
│ │ │ │ ├── forwardRef.md
│ │ │ │ ├── hooks.md
│ │ │ │ ├── index.md
│ │ │ │ ├── isValidElement.md
│ │ │ │ ├── lazy.md
│ │ │ │ ├── legacy.md
│ │ │ │ ├── memo.md
│ │ │ │ ├── startTransition.md
│ │ │ │ ├── use.md
│ │ │ │ ├── useActionState.md
│ │ │ │ ├── useCallback.md
│ │ │ │ ├── useContext.md
│ │ │ │ ├── useDebugValue.md
│ │ │ │ ├── useDeferredValue.md
│ │ │ │ ├── useEffect.md
│ │ │ │ ├── useEffectEvent.md
│ │ │ │ ├── useId.md
│ │ │ │ ├── useImperativeHandle.md
│ │ │ │ ├── useInsertionEffect.md
│ │ │ │ ├── useLayoutEffect.md
│ │ │ │ ├── useMemo.md
│ │ │ │ ├── useOptimistic.md
│ │ │ │ ├── useReducer.md
│ │ │ │ ├── useRef.md
│ │ │ │ ├── useState.md
│ │ │ │ ├── useSyncExternalStore.md
│ │ │ │ └── useTransition.md
│ │ │ ├── react-compiler/
│ │ │ │ ├── compilationMode.md
│ │ │ │ ├── compiling-libraries.md
│ │ │ │ ├── configuration.md
│ │ │ │ ├── directives/
│ │ │ │ │ ├── use-memo.md
│ │ │ │ │ └── use-no-memo.md
│ │ │ │ ├── directives.md
│ │ │ │ ├── gating.md
│ │ │ │ ├── logger.md
│ │ │ │ ├── panicThreshold.md
│ │ │ │ └── target.md
│ │ │ ├── react-dom/
│ │ │ │ ├── client/
│ │ │ │ │ ├── createRoot.md
│ │ │ │ │ ├── hydrateRoot.md
│ │ │ │ │ └── index.md
│ │ │ │ ├── components/
│ │ │ │ │ ├── common.md
│ │ │ │ │ ├── form.md
│ │ │ │ │ ├── index.md
│ │ │ │ │ ├── input.md
│ │ │ │ │ ├── link.md
│ │ │ │ │ ├── meta.md
│ │ │ │ │ ├── option.md
│ │ │ │ │ ├── progress.md
│ │ │ │ │ ├── script.md
│ │ │ │ │ ├── select.md
│ │ │ │ │ ├── style.md
│ │ │ │ │ ├── textarea.md
│ │ │ │ │ └── title.md
│ │ │ │ ├── createPortal.md
│ │ │ │ ├── flushSync.md
│ │ │ │ ├── hooks/
│ │ │ │ │ ├── index.md
│ │ │ │ │ └── useFormStatus.md
│ │ │ │ ├── index.md
│ │ │ │ ├── preconnect.md
│ │ │ │ ├── prefetchDNS.md
│ │ │ │ ├── preinit.md
│ │ │ │ ├── preinitModule.md
│ │ │ │ ├── preload.md
│ │ │ │ ├── preloadModule.md
│ │ │ │ ├── server/
│ │ │ │ │ ├── index.md
│ │ │ │ │ ├── renderToPipeableStream.md
│ │ │ │ │ ├── renderToReadableStream.md
│ │ │ │ │ ├── renderToStaticMarkup.md
│ │ │ │ │ ├── renderToString.md
│ │ │ │ │ ├── resume.md
│ │ │ │ │ └── resumeToPipeableStream.md
│ │ │ │ └── static/
│ │ │ │ ├── index.md
│ │ │ │ ├── prerender.md
│ │ │ │ ├── prerenderToNodeStream.md
│ │ │ │ ├── resumeAndPrerender.md
│ │ │ │ └── resumeAndPrerenderToNodeStream.md
│ │ │ ├── rsc/
│ │ │ │ ├── directives.md
│ │ │ │ ├── server-components.md
│ │ │ │ ├── server-functions.md
│ │ │ │ ├── use-client.md
│ │ │ │ └── use-server.md
│ │ │ └── rules/
│ │ │ ├── components-and-hooks-must-be-pure.md
│ │ │ ├── index.md
│ │ │ ├── react-calls-components-and-hooks.md
│ │ │ └── rules-of-hooks.md
│ │ ├── versions.md
│ │ └── warnings/
│ │ ├── invalid-aria-prop.md
│ │ ├── invalid-hook-call-warning.md
│ │ ├── react-dom-test-utils.md
│ │ ├── react-test-renderer.md
│ │ ├── special-props.md
│ │ └── unknown-prop.md
│ ├── hooks/
│ │ └── usePendingRoute.ts
│ ├── pages/
│ │ ├── 404.js
│ │ ├── 500.js
│ │ ├── [[...markdownPath]].js
│ │ ├── _app.tsx
│ │ ├── _document.tsx
│ │ ├── api/
│ │ │ └── md/
│ │ │ └── [...path].ts
│ │ ├── errors/
│ │ │ ├── [errorCode].tsx
│ │ │ └── index.tsx
│ │ └── llms.txt.tsx
│ ├── sidebarBlog.json
│ ├── sidebarCommunity.json
│ ├── sidebarHome.json
│ ├── sidebarLearn.json
│ ├── sidebarReference.json
│ ├── siteConfig.js
│ ├── styles/
│ │ ├── algolia.css
│ │ ├── index.css
│ │ └── sandpack.css
│ ├── types/
│ │ └── docsearch-react-modal.d.ts
│ └── utils/
│ ├── analytics.ts
│ ├── compileMDX.ts
│ ├── finishedTranslations.ts
│ ├── forwardRefWithAs.tsx
│ ├── prepareMDX.js
│ ├── processShim.js
│ ├── rafShim.js
│ ├── rss.js
│ └── toCommaSeparatedList.tsx
├── tailwind.config.js
├── textlint/
│ ├── data/
│ │ ├── rules/
│ │ │ └── translateGlossary.js
│ │ └── utils/
│ │ ├── errMsg.spec.js
│ │ ├── is.spec.js
│ │ └── strip.spec.js
│ ├── generators/
│ │ └── genTranslateGlossaryDocs.js
│ ├── rules/
│ │ └── translateGlossary.js
│ ├── tests/
│ │ ├── rules/
│ │ │ └── translateGlossary.spec.js
│ │ └── utils/
│ │ ├── errMsg.spec.js
│ │ ├── is.spec.js
│ │ └── strip.spec.js
│ └── utils/
│ ├── errMsg.js
│ ├── is.js
│ └── strip.js
├── tsconfig.json
├── vercel.json
└── wiki/
├── best-practices-for-translation.md
├── textlint-guide.md
├── translate-glossary-legacy.md
├── translate-glossary.md
└── universal-style-guide.md
================================================
FILE CONTENTS
================================================
================================================
FILE: .claude/agents/docs-reviewer.md
================================================
---
name: docs-reviewer
description: "Lean docs reviewer that dispatches reviews docs for a particular skill."
model: opus
color: cyan
---
You are a direct, critical, expert reviewer for React documentation.
Your role is to use given skills to validate given doc pages for consistency, correctness, and adherence to established patterns.
Complete this process:
## Phase 1: Task Creation
1. CRITICAL: Read the skill requested.
2. Understand the skill's requirements.
3. Create a task list to validate skills requirements.
## Phase 2: Validate
1. Read the docs files given.
2. Review each file with the task list to verify.
## Phase 3: Respond
You must respond with a checklist of the issues you identified, and line number.
DO NOT respond with passed validations, ONLY respond with the problems.
================================================
FILE: .claude/settings.json
================================================
{
"skills": {
"suggest": [
{
"pattern": "src/content/learn/**/*.md",
"skill": "docs-writer-learn"
},
{
"pattern": "src/content/reference/**/*.md",
"skill": "docs-writer-reference"
}
]
},
"permissions": {
"allow": [
"Skill(docs-voice)",
"Skill(docs-components)",
"Skill(docs-sandpack)",
"Skill(docs-rsc-sandpack)",
"Skill(docs-writer-learn)",
"Skill(docs-writer-reference)",
"Bash(yarn lint:*)",
"Bash(yarn lint-heading-ids:*)",
"Bash(yarn lint:fix:*)",
"Bash(yarn tsc:*)",
"Bash(yarn check-all:*)",
"Bash(yarn fix-headings:*)",
"Bash(yarn deadlinks:*)",
"Bash(yarn prettier:diff:*)"
]
}
}
================================================
FILE: .claude/skills/docs-components/SKILL.md
================================================
---
name: docs-components
description: Comprehensive MDX component patterns (Note, Pitfall, DeepDive, Recipes, etc.) for all documentation types. Authoritative source for component usage, examples, and heading conventions.
---
# MDX Component Patterns
## Quick Reference
### Component Decision Tree
| Need | Component |
|------|-----------|
| Helpful tip or terminology | `` |
| Common mistake warning | `` |
| Advanced technical explanation | `` |
| Canary-only feature | `` or `` |
| Server Components only | `` |
| Deprecated API | `` |
| Experimental/WIP | `` |
| Visual diagram | `` |
| Multiple related examples | `` |
| Interactive code | `` (see `/docs-sandpack`) |
| Console error display | `` |
| End-of-page exercises | `` (Learn pages only) |
### Heading Level Conventions
| Component | Heading Level |
|-----------|---------------|
| DeepDive title | `####` (h4) |
| Titled Pitfall | `#####` (h5) |
| Titled Note | `####` (h4) |
| Recipe items | `####` (h4) |
| Challenge items | `####` (h4) |
### Callout Spacing Rules
Callout components (Note, Pitfall, DeepDive) require a **blank line after the opening tag** before content begins.
**Never place consecutively:**
- `` followed by `` - Combine into one with titled subsections, or separate with prose
- `` followed by `` - Combine into one, or separate with prose
**Allowed consecutive patterns:**
- `` followed by `` - OK for multi-part explorations (see useMemo.md)
- `` followed by `` - OK when DeepDive explains "why" behind the Pitfall
**Separation content:** Prose paragraphs, code examples (Sandpack), or section headers.
**Why:** Consecutive warnings create a "wall of cautions" that overwhelms readers and causes important warnings to be skimmed.
**Incorrect:**
```mdx
Don't do X.
Don't do Y.
```
**Correct - combined:**
```mdx
##### Don't do X {/*pitfall-x*/}
Explanation.
##### Don't do Y {/*pitfall-y*/}
Explanation.
```
**Correct - separated:**
```mdx
Don't do X.
This leads to another common mistake:
Don't do Y.
```
---
## ``
Important clarifications, conventions, or tips. Less severe than Pitfall.
### Simple Note
```mdx
The optimization of caching return values is known as [_memoization_](https://en.wikipedia.org/wiki/Memoization).
```
### Note with Title
Use `####` (h4) heading with an ID.
```mdx
#### There is no directive for Server Components. {/*no-directive*/}
A common misunderstanding is that Server Components are denoted by `"use server"`, but there is no directive for Server Components. The `"use server"` directive is for Server Functions.
```
### Version-Specific Note
```mdx
Starting in React 19, you can render `` as a provider.
In older versions of React, use ``.
```
---
## ``
Common mistakes that cause bugs. Use for errors readers will likely make.
### Simple Pitfall
```mdx
We recommend defining components as functions instead of classes. [See how to migrate.](#alternatives)
```
### Titled Pitfall
Use `#####` (h5) heading with an ID.
```mdx
##### Calling different memoized functions will read from different caches. {/*pitfall-different-caches*/}
To access the same cache, components must call the same memoized function.
```
### Pitfall with Wrong/Right Code
```mdx
##### `useFormStatus` will not return status information for a `;
}
```
Instead call `useFormStatus` from inside a component located inside `
```
---
## ``
Optional deep technical content. **First child must be `####` heading with ID.**
### Standard DeepDive
```mdx
#### Is using an updater always preferred? {/*is-updater-preferred*/}
You might hear a recommendation to always write code like `setAge(a => a + 1)` if the state you're setting is calculated from the previous state. There's no harm in it, but it's also not always necessary.
In most cases, there is no difference between these two approaches. React always makes sure that for intentional user actions, like clicks, the `age` state variable would be updated before the next click.
```
### Comparison DeepDive
For comparing related concepts:
```mdx
#### When should I use `cache`, `memo`, or `useMemo`? {/*cache-memo-usememo*/}
All mentioned APIs offer memoization but differ in what they memoize, who can access the cache, and when their cache is invalidated.
#### `useMemo` {/*deep-dive-usememo*/}
In general, you should use `useMemo` for caching expensive computations in Client Components across renders.
#### `cache` {/*deep-dive-cache*/}
In general, you should use `cache` in Server Components to memoize work that can be shared across components.
```
---
## ``
Multiple related examples showing variations. Each recipe needs ``.
```mdx
#### Counter (number) {/*counter-number*/}
In this example, the `count` state variable holds a number.
{/* code */}
#### Text field (string) {/*text-field-string*/}
In this example, the `text` state variable holds a string.
{/* code */}
```
**Common titleText/titleId combinations:**
- "Basic [hookName] examples" / `examples-basic`
- "Examples of [concept]" / `examples-[concept]`
- "The difference between [A] and [B]" / `examples-[topic]`
---
## ``
End-of-page exercises. **Learn pages only.** Each challenge needs problem + solution Sandpack.
```mdx
#### Fix the bug {/*fix-the-bug*/}
Problem description...
Optional hint text.
{/* problem code */}
Explanation...
{/* solution code */}
```
**Guidelines:**
- Only at end of standard Learn pages
- No Challenges in chapter intros or tutorials
- Each challenge has `####` heading with ID
---
## ``
For deprecated APIs. Content should explain what to use instead.
### Page-Level Deprecation
```mdx
In React 19, `forwardRef` is no longer necessary. Pass `ref` as a prop instead.
`forwardRef` will be deprecated in a future release. Learn more [here](/blog/2024/04/25/react-19#ref-as-a-prop).
```
### Method-Level Deprecation
```mdx
### `componentWillMount()` {/*componentwillmount*/}
This API has been renamed from `componentWillMount` to [`UNSAFE_componentWillMount`.](#unsafe_componentwillmount)
Run the [`rename-unsafe-lifecycles` codemod](codemod-link) to automatically update.
```
---
## ``
For APIs that only work with React Server Components.
### Basic RSC
```mdx
`cache` is only for use with [React Server Components](/reference/rsc/server-components).
```
### Extended RSC (for Server Functions)
```mdx
Server Functions are for use in [React Server Components](/reference/rsc/server-components).
**Note:** Until September 2024, we referred to all Server Functions as "Server Actions".
```
---
## `` and ``
For features only available in Canary releases.
### Canary Wrapper (inline in Intro)
```mdx
`` lets you group elements without a wrapper node.
Fragments can also accept refs, enabling interaction with underlying DOM nodes.
```
### CanaryBadge in Section Headings
```mdx
### FragmentInstance {/*fragmentinstance*/}
```
### CanaryBadge in Props Lists
```mdx
* **optional** `ref`: A ref object from `useRef` or callback function.
```
### CanaryBadge in Caveats
```mdx
* If you want to pass `ref` to a Fragment, you can't use the `<>...>` syntax.
```
---
## ``
Visual explanations of module dependencies, render trees, or data flow.
```mdx
`'use client'` segments the module dependency tree, marking `InspirationGenerator.js` and all dependencies as client-rendered.
```
**Attributes:**
- `name`: Diagram identifier (used for image file)
- `height`: Height in pixels
- `width`: Width in pixels
- `alt`: Accessible description of the diagram
---
## `` (Use Sparingly)
Numbered callouts in prose. Pairs with code block annotations.
### Syntax
In code blocks:
```mdx
```js [[1, 4, "age"], [2, 4, "setAge"], [3, 4, "42"]]
import { useState } from 'react';
function MyComponent() {
const [age, setAge] = useState(42);
}
```
```
Format: `[[step_number, line_number, "text_to_highlight"], ...]`
In prose:
```mdx
1. The current state initially set to the initial value.
2. The `set` function that lets you change it.
```
### Guidelines
- Maximum 2-3 different colors per explanation
- Don't highlight every keyword - only key concepts
- Use for terms in prose, not entire code blocks
- Maintain consistent usage within a section
✅ **Good use** - highlighting key concepts:
```mdx
React will compare the dependencies with the dependencies you passed...
```
🚫 **Avoid** - excessive highlighting:
```mdx
When an Activity boundary is hidden during its initial render...
```
---
## ``
Display console output (errors, warnings, logs).
```mdx
Uncaught Error: Too many re-renders.
```
**Levels:** `error`, `warning`, `info`
---
## Component Usage by Page Type
### Reference Pages
For component placement rules specific to Reference pages, invoke `/docs-writer-reference`.
Key placement patterns:
- `` goes before `` at top of page
- `` goes after `` for page-level deprecation
- `` goes after method heading for method-level deprecation
- `` wrapper goes inline within ``
- `` appears in headings, props lists, and caveats
### Learn Pages
For Learn page structure and patterns, invoke `/docs-writer-learn`.
Key usage patterns:
- Challenges only at end of standard Learn pages
- No Challenges in chapter intros or tutorials
- DeepDive for optional advanced content
- CodeStep should be used sparingly
### Blog Pages
For Blog page structure and patterns, invoke `/docs-writer-blog`.
Key usage patterns:
- Generally avoid deep technical components
- Note and Pitfall OK for clarifications
- Prefer inline explanations over DeepDive
---
## Other Available Components
**Version/Status:** ``, ``, ``, ``, ``
**Visuals:** ``, ``, ``, ``, ``
**Console:** ``, ``
**Specialized:** ``, ``, ``, ``, ``, ``, `
}>
);
}
` ` `
` ` `js src/Albums.js
async function fetchAlbums() {
await new Promise(resolve => setTimeout(resolve, 1000));
return ['Abbey Road', 'Let It Be', 'Revolver'];
}
export default async function Albums() {
const albums = await fetchAlbums();
return (
{albums.map(album => (
{album}
))}
);
}
` ` `
```
### 3. Server Functions (Actions)
```mdx
` ` `js src/App.js
import { addLike, getLikeCount } from './actions';
import LikeButton from './LikeButton';
export default async function App() {
const count = await getLikeCount();
return (
Likes: {count}
);
}
` ` `
` ` `js src/actions.js
'use server';
let count = 0;
export async function addLike() {
count++;
}
export async function getLikeCount() {
return count;
}
` ` `
` ` `js src/LikeButton.js
'use client';
export default function LikeButton({ addLike }) {
return (
);
}
` ` `
```
---
## File Structure Requirements
### Entry Point
- **`src/App.js` is required** as the main entry point
- Must have `export default` (function component)
- Case-insensitive fallback: `src/app.js` also works
### Auto-Injected Infrastructure Files
These files are automatically injected by `sandpack-rsc-setup.ts` and should never be included in MDX:
| File | Purpose |
|------|---------|
| `/src/index.js` | Bootstraps the RSC pipeline |
| `/src/rsc-client.js` | Client bridge — creates Worker, consumes Flight stream |
| `/src/rsc-server.js` | Wraps pre-bundled worker runtime as ES module |
| `/node_modules/__webpack_shim__/index.js` | Minimal webpack compatibility layer |
| `/node_modules/__rsdw_client__/index.js` | `react-server-dom-webpack/client` as local dependency |
### No External Dependencies
`` does not support external npm packages. Only `react` and `react-dom` are available. Do not include `package.json` in RSC examples.
---
## Architecture Reference
### Three-Layer Architecture
```
react.dev page (Next.js)
┌─────────────────────────────────────────┐
│ │
│ ┌─────────┐ ┌──────────────────────┐ │
│ │ Editor │ │ Preview (iframe) │ │
│ │ App.js │ │ Client React app │ │
│ │ (edit) │ │ consumes Flight │ │
│ │ │ │ stream from Worker │ │
│ └─────────┘ └──────────┬───────────┘ │
└───────────────────────────┼─────────────┘
│ postMessage
┌───────────────────────────▼─────────────┐
│ Web Worker (Blob URL) │
│ - React server build (pre-bundled) │
│ - react-server-dom-webpack/server │
│ - webpack shim │
│ - User server code (Sucrase → CJS) │
└─────────────────────────────────────────┘
```
### Key Source Files
| File | Purpose |
|-----------------------------------------------------------------|--------------------------------------------------------------------------------|
| `src/components/MDX/Sandpack/sandpack-rsc/RscFileBridge.tsx` | Monitors Sandpack; posts raw files to iframe |
| `src/components/MDX/Sandpack/SandpackRSCRoot.tsx` | SandpackProvider setup, custom bundler URL, UI layout |
| `src/components/MDX/Sandpack/templateRSC.ts` | RSC template files |
| `.../sandbox-code/src/__react_refresh_init__.js` | React Refresh shim |
| `.../sandbox-code/src/rsc-server.js` | Worker runtime: module system, Sucrase compilation, `renderToReadableStream()` |
| `.../sandbox-code/src/rsc-client.source.js` | Client bridge: Worker creation, file classification, Flight stream consumption |
| `.../sandbox-code/src/webpack-shim.js` | Minimal `__webpack_require__` / `__webpack_module_cache__` shim |
| `.../sandbox-code/src/worker-bundle.dist.js` | Pre-bundled IIFE (generated): React server + RSDW/server + Sucrase |
| `scripts/buildRscWorker.mjs` | esbuild script: bundles rsc-server.js into worker-bundle.dist.js |
---
## Build System
### Rebuilding the Worker Bundle
After modifying `rsc-server.js` or `webpack-shim.js`:
```bash
node scripts/buildRscWorker.mjs
```
This runs esbuild with:
- `format: 'iife'`, `platform: 'browser'`
- `conditions: ['react-server', 'browser']` (activates React server export conditions)
- `minify: true`
- Prepends `webpack-shim.js` to the output
### Raw-Loader Configuration
In `templateRSC.js` files are loaded as raw strings with the `!raw-loader`.
The strings are necessary to provide to Sandpack as local files (skips Sandpack bundling).
### Development Commands
```bash
node scripts/buildRscWorker.mjs # Rebuild worker bundle after source changes
yarn dev # Start dev server to test examples
```
================================================
FILE: .claude/skills/docs-sandpack/SKILL.md
================================================
---
name: docs-sandpack
description: Use when adding interactive code examples to React docs.
---
# Sandpack Patterns
## Quick Start Template
Most examples are single-file. Copy this and modify:
```mdx
` ` `js
import { useState } from 'react';
export default function Example() {
const [value, setValue] = useState(0);
return (
);
}
` ` `
```
---
## File Naming
| Pattern | Usage |
|---------|-------|
| ` ```js ` | Main file (no prefix) |
| ` ```js src/FileName.js ` | Supporting files |
| ` ```js src/File.js active ` | Active file (reference pages) |
| ` ```js src/data.js hidden ` | Hidden files |
| ` ```css ` | CSS styles |
| ` ```json package.json ` | External dependencies |
**Critical:** Main file must have `export default`.
## Line Highlighting
```mdx
```js {2-4}
function Example() {
// Lines 2-4
// will be
// highlighted
return null;
}
```
## Code References (numbered callouts)
```mdx
```js [[1, 4, "age"], [2, 4, "setAge"]]
// Creates numbered markers pointing to "age" and "setAge" on line 4
```
## Expected Errors (intentionally broken examples)
```mdx
```js {expectedErrors: {'react-compiler': [7]}}
// Line 7 shows as expected error
```
## Multi-File Example
```mdx
```js src/App.js
import Gallery from './Gallery.js';
export default function App() {
return ;
}
```
```js src/Gallery.js
export default function Gallery() {
return
Gallery
;
}
```
```css
h1 { color: purple; }
```
```
## External Dependencies
```mdx
```js
import { useImmer } from 'use-immer';
// ...
```
```json package.json
{
"dependencies": {
"immer": "1.7.3",
"use-immer": "0.5.1",
"react": "latest",
"react-dom": "latest",
"react-scripts": "latest"
}
}
```
```
## Code Style in Sandpack (Required)
Sandpack examples are held to strict code style standards:
1. **Function declarations** for components (not arrows)
2. **`e`** for event parameters
3. **Single quotes** in JSX
4. **`const`** unless reassignment needed
5. **Spaces in destructuring**: `({ props })` not `({props})`
6. **Two-line createRoot**: separate declaration and render call
7. **Multiline if statements**: always use braces
### Don't Create Hydration Mismatches
Sandpack examples must produce the same output on server and client:
```js
// 🚫 This will cause hydration warnings
export default function App() {
const isClient = typeof window !== 'undefined';
return
{isClient ? 'Client' : 'Server'}
;
}
```
### Use Ref for Non-Rendered State
```js
// 🚫 Don't trigger re-renders for non-visual state
const [mounted, setMounted] = useState(false);
useEffect(() => { setMounted(true); }, []);
// ✅ Use ref instead
const mounted = useRef(false);
useEffect(() => { mounted.current = true; }, []);
```
## forwardRef and memo Patterns
### forwardRef - Use Named Function
```js
// ✅ Named function for DevTools display name
const MyInput = forwardRef(function MyInput(props, ref) {
return ;
});
// 🚫 Anonymous loses name
const MyInput = forwardRef((props, ref) => { ... });
```
### memo - Use Named Function
```js
// ✅ Preserves component name
const Greeting = memo(function Greeting({ name }) {
return
Hello, {name}
;
});
```
## Line Length
- Prose: ~80 characters
- Code: ~60-70 characters
- Break long lines to avoid horizontal scrolling
## Anti-Patterns
| Pattern | Problem | Fix |
|---------|---------|-----|
| `const Comp = () => {}` | Not standard | `function Comp() {}` |
| `onClick={(event) => ...}` | Conflicts with global | `onClick={(e) => ...}` |
| `useState` for non-rendered values | Re-renders | Use `useRef` |
| Reading `window` during render | Hydration mismatch | Check in useEffect |
| Single-line if without braces | Harder to debug | Use multiline with braces |
| Chained `createRoot().render()` | Less clear | Two statements |
| `//...` without space | Inconsistent | `// ...` with space |
| Tabs | Inconsistent | 2 spaces |
| `ReactDOM.render` | Deprecated | Use `createRoot` |
| Fake package names | Confusing | Use `'./your-storage-layer'` |
| `PropsWithChildren` | Outdated | `children?: ReactNode` |
| Missing `key` in lists | Warnings | Always include key |
## Additional Code Quality Rules
### Always Include Keys in Lists
```js
// ✅ Correct
{items.map(item =>
{item.name}
)}
// 🚫 Wrong - missing key
{items.map(item =>
{item.name}
)}
```
### Use Realistic Import Paths
```js
// ✅ Correct - descriptive path
import { fetchData } from './your-data-layer';
// 🚫 Wrong - looks like a real npm package
import { fetchData } from 'cool-data-lib';
```
### Console.log Labels
```js
// ✅ Correct - labeled for clarity
console.log('User:', user);
console.log('Component Stack:', errorInfo.componentStack);
// 🚫 Wrong - unlabeled
console.log(user);
```
### Keep Delays Reasonable
```js
// ✅ Correct - 1-1.5 seconds
setTimeout(() => setLoading(false), 1000);
// 🚫 Wrong - too long, feels sluggish
setTimeout(() => setLoading(false), 3000);
```
## Updating Line Highlights
When modifying code in examples with line highlights (`{2-4}`), **always update the highlight line numbers** to match the new code. Incorrect line numbers cause rendering crashes.
## File Name Conventions
- Capitalize file names for component files: `Gallery.js` not `gallery.js`
- After initially explaining files are in `src/`, refer to files by name only: `Gallery.js` not `src/Gallery.js`
## Naming Conventions in Code
**Components:** PascalCase
- `Profile`, `Avatar`, `TodoList`, `PackingList`
**State variables:** Destructured pattern
- `const [count, setCount] = useState(0)`
- Booleans: `[isOnline, setIsOnline]`, `[isPacked, setIsPacked]`
- Status strings: `'typing'`, `'submitting'`, `'success'`, `'error'`
**Event handlers:**
- `handleClick`, `handleSubmit`, `handleAddTask`
**Props for callbacks:**
- `onClick`, `onChange`, `onAddTask`, `onSelect`
**Custom Hooks:**
- `useOnlineStatus`, `useChatRoom`, `useFormInput`
**Reducer actions:**
- Past tense: `'added'`, `'changed'`, `'deleted'`
- Snake_case compounds: `'changed_selection'`, `'sent_message'`
**Updater functions:** Single letter
- `setCount(n => n + 1)`
### Pedagogical Code Markers
**Wrong vs right code:**
```js
// 🔴 Avoid: redundant state and unnecessary Effect
// ✅ Good: calculated during rendering
```
**Console.log for lifecycle teaching:**
```js
console.log('✅ Connecting...');
console.log('❌ Disconnected.');
```
### Server/Client Labeling
```js
// Server Component
async function Notes() {
const notes = await db.notes.getAll();
}
// Client Component
"use client"
export default function Expandable({children}) {
const [expanded, setExpanded] = useState(false);
}
```
### Bundle Size Annotations
```js
import marked from 'marked'; // 35.9K (11.2K gzipped)
import sanitizeHtml from 'sanitize-html'; // 206K (63.3K gzipped)
```
---
## Sandpack Example Guidelines
### Package.json Rules
**Include package.json when:**
- Using external npm packages (immer, remarkable, leaflet, toastify-js, etc.)
- Demonstrating experimental/canary React features
- Requiring specific React versions (`react: beta`, `react: 19.0.0-rc-*`)
**Omit package.json when:**
- Example uses only built-in React features
- No external dependencies needed
- Teaching basic hooks, state, or components
**Always mark package.json as hidden:**
```mdx
```json package.json hidden
{
"dependencies": {
"react": "latest",
"react-dom": "latest",
"react-scripts": "latest",
"immer": "1.7.3"
}
}
```
```
**Version conventions:**
- Use `"latest"` for stable features
- Use exact versions only when compatibility requires it
- Include minimal dependencies (just what the example needs)
### Hidden File Patterns
**Always hide these file types:**
| File Type | Reason |
|-----------|--------|
| `package.json` | Configuration not the teaching point |
| `sandbox.config.json` | Sandbox setup is boilerplate |
| `public/index.html` | HTML structure not the focus |
| `src/data.js` | When it contains sample/mock data |
| `src/api.js` | When showing API usage, not implementation |
| `src/styles.css` | When styling is not the lesson |
| `src/router.js` | Supporting infrastructure |
| `src/actions.js` | Server action implementation details |
**Rationale:**
- Reduces cognitive load
- Keeps focus on the primary concept
- Creates cleaner, more focused examples
**Example:**
```mdx
```js src/data.js hidden
export const items = [
{ id: 1, name: 'Item 1' },
{ id: 2, name: 'Item 2' },
];
```
```
### Active File Patterns
**Mark as active when:**
- File contains the primary teaching concept
- Learner should focus on this code first
- Component demonstrates the hook/pattern being taught
**Effect of the `active` marker:**
- Sets initial editor tab focus when Sandpack loads
- Signals "this is what you should study"
- Works with hidden files to create focused examples
**Most common active file:** `src/index.js` or `src/App.js`
**Example:**
```mdx
```js src/App.js active
// This file will be focused when example loads
export default function App() {
// ...
}
```
```
### File Structure Guidelines
| Scenario | Structure | Reason |
|----------|-----------|--------|
| Basic hook usage | Single file | Simple, focused |
| Teaching imports | 2-3 files | Shows modularity |
| Context patterns | 4-5 files | Realistic structure |
| Complex state | 3+ files | Separation of concerns |
**Single File Examples (70% of cases):**
- Use for simple concepts
- 50-200 lines typical
- Best for: Counter, text inputs, basic hooks
**Multi-File Examples (30% of cases):**
- Use when teaching modularity/imports
- Use for context patterns (4-5 files)
- Use when component is reused
**File Naming:**
- Main component: `App.js` (capitalized)
- Component files: `Gallery.js`, `Button.js` (capitalized)
- Data files: `data.js` (lowercase)
- Utility files: `utils.js` (lowercase)
- Context files: `TasksContext.js` (named after what they provide)
### Code Size Limits
- Single file: **<200 lines**
- Multi-file total: **150-300 lines**
- Main component: **100-150 lines**
- Supporting files: **20-40 lines each**
### CSS Guidelines
**Always:**
- Include minimal CSS for demo interactivity
- Use semantic class names (`.panel`, `.button-primary`, `.panel-dark`)
- Support light/dark themes when showing UI concepts
- Keep CSS visible (never hidden)
**Size Guidelines:**
- Minimal (5-10 lines): Basic button styling, spacing
- Medium (15-30 lines): Panel styling, form layouts
- Complex (40+ lines): Only for layout-focused examples
================================================
FILE: .claude/skills/docs-voice/SKILL.md
================================================
---
name: docs-voice
description: Use when writing any React documentation. Provides voice, tone, and style rules for all doc types.
---
# React Docs Voice & Style
## Universal Rules
- **Capitalize React terms** when referring to the React concept in headings or as standalone concepts:
- Core: Hook, Effect, State, Context, Ref, Component, Fragment
- Concurrent: Transition, Action, Suspense
- Server: Server Component, Client Component, Server Function, Server Action
- Patterns: Error Boundary
- Canary: Activity, View Transition, Transition Type
- **In prose:** Use lowercase when paired with descriptors: "state variable", "state updates", "event handler". Capitalize when the concept stands alone or in headings: "State is isolated and private"
- General usage stays lowercase: "the page transitions", "takes an action"
- **Product names:** ESLint, TypeScript, JavaScript, Next.js (not lowercase)
- **Bold** for key concepts: **state variable**, **event handler**
- **Italics** for new terms being defined: *event handlers*
- **Inline code** for APIs: `useState`, `startTransition`, ``
- **Avoid:** "simple", "easy", "just", time estimates
- Frame differences as "capabilities" not "advantages/disadvantages"
- Avoid passive voice and jargon
## Tone by Page Type
| Type | Tone | Example |
|------|------|---------|
| Learn | Conversational | "Here's what that looks like...", "You might be wondering..." |
| Reference | Technical | "Call `useState` at the top level...", "This Hook returns..." |
| Blog | Accurate | Focus on facts, not marketing |
**Note:** Pitfall and DeepDive components can use slightly more conversational phrasing ("You might wonder...", "It might be tempting...") even in Reference pages, since they're explanatory asides.
## Avoiding Jargon
**Pattern:** Explain behavior first, then name it.
✅ "React waits until all code in event handlers runs before processing state updates. This is called *batching*."
❌ "React uses batching to process state updates atomically."
**Terms to avoid or explain:**
| Jargon | Plain Language |
|--------|----------------|
| atomic | all-or-nothing, batched together |
| idempotent | same inputs, same output |
| deterministic | predictable, same result every time |
| memoize | remember the result, skip recalculating |
| referentially transparent | (avoid - describe the behavior) |
| invariant | rule that must always be true |
| reify | (avoid - describe what's being created) |
**Allowed technical terms in Reference pages:**
- "stale closures" - standard JS/React term, can be used in Caveats
- "stable identity" - React term for consistent object references across renders
- "reactive" - React term for values that trigger re-renders when changed
- These don't need explanation in Reference pages (readers are expected to know them)
**Use established analogies sparingly—once when introducing a concept, not repeatedly:**
| Concept | Analogy |
|---------|---------|
| Components/React | Kitchen (components as cooks, React as waiter) |
| Render phases | Restaurant ordering (trigger/render/commit) |
| State batching | Waiter collecting full order before going to kitchen |
| State behavior | Snapshot/photograph in time |
| State storage | React storing state "on a shelf" |
| State purpose | Component's memory |
| Pure functions | Recipes (same ingredients → same dish) |
| Pure functions | Math formulas (y = 2x) |
| Props | Adjustable "knobs" |
| Children prop | "Hole" to be filled by parent |
| Keys | File names in a folder |
| Curly braces in JSX | "Window into JavaScript" |
| Declarative UI | Taxi driver (destination, not turn-by-turn) |
| Imperative UI | Turn-by-turn navigation |
| State structure | Database normalization |
| Refs | "Secret pocket" React doesn't track |
| Effects/Refs | "Escape hatch" from React |
| Context | CSS inheritance / "Teleportation" |
| Custom Hooks | Design system |
## Common Prose Patterns
**Wrong vs Right code:**
```mdx
\`\`\`js
// 🚩 Don't mutate state:
obj.x = 10;
\`\`\`
\`\`\`js
// ✅ Replace with new object:
setObj({ ...obj, x: 10 });
\`\`\`
```
**Table comparisons:**
```mdx
| passing a function | calling a function |
| `onClick={handleClick}` | `onClick={handleClick()}` |
```
**Linking:**
```mdx
[Read about state](/learn/state-a-components-memory)
[See `useState` reference](/reference/react/useState)
```
## Code Style
- Prefer JSX over createElement
- Use const/let, never var
- Prefer named function declarations for top-level functions
- Arrow functions for callbacks that need `this` preservation
## Version Documentation
When APIs change between versions:
```mdx
Starting in React 19, render `` as a provider:
\`\`\`js
{children}
\`\`\`
In older versions:
\`\`\`js
{children}
\`\`\`
```
Patterns:
- "Starting in React 19..." for new APIs
- "In older versions of React..." for legacy patterns
================================================
FILE: .claude/skills/docs-writer-blog/SKILL.md
================================================
---
name: docs-writer-blog
description: Use when writing or editing files in src/content/blog/. Provides blog post structure and conventions.
---
# Blog Post Writer
## Persona
**Voice:** Official React team voice
**Tone:** Accurate, professional, forward-looking
## Voice & Style
For tone, capitalization, jargon, and prose patterns, invoke `/docs-voice`.
---
## Frontmatter Schema
All blog posts use this YAML frontmatter structure:
```yaml
---
title: "Title in Quotes"
author: Author Name(s)
date: YYYY/MM/DD
description: One or two sentence summary.
---
```
### Field Details
| Field | Format | Example |
|-------|--------|---------|
| `title` | Quoted string | `"React v19"`, `"React Conf 2024 Recap"` |
| `author` | Unquoted, comma + "and" for multiple | `The React Team`, `Dan Abramov and Lauren Tan` |
| `date` | `YYYY/MM/DD` with forward slashes | `2024/12/05` |
| `description` | 1-2 sentences, often mirrors intro | Summarizes announcement or content |
### Title Patterns by Post Type
| Type | Pattern | Example |
|------|---------|---------|
| Release | `"React vX.Y"` or `"React X.Y"` | `"React v19"` |
| Upgrade | `"React [VERSION] Upgrade Guide"` | `"How to Upgrade to React 18"` |
| Labs | `"React Labs: [Topic] – [Month Year]"` | `"React Labs: What We've Been Working On – February 2024"` |
| Conf | `"React Conf [YEAR] Recap"` | `"React Conf 2024 Recap"` |
| Feature | `"Introducing [Feature]"` or descriptive | `"Introducing react.dev"` |
| Security | `"[Severity] Security Vulnerability in [Component]"` | `"Critical Security Vulnerability in React Server Components"` |
---
## Author Byline
Immediately after frontmatter, add a byline:
```markdown
---
Month DD, YYYY by [Author Name](social-link)
---
```
### Conventions
- Full date spelled out: `December 05, 2024`
- Team posts link to `/community/team`: `[The React Team](/community/team)`
- Individual authors link to Twitter/X or Bluesky
- Multiple authors: Oxford comma before "and"
- Followed by horizontal rule `---`
**Examples:**
```markdown
December 05, 2024 by [The React Team](/community/team)
---
```
```markdown
May 3, 2023 by [Dan Abramov](https://bsky.app/profile/danabra.mov), [Sophie Alpert](https://twitter.com/sophiebits), and [Andrew Clark](https://twitter.com/acdlite)
---
```
---
## Universal Post Structure
All blog posts follow this structure:
1. **Frontmatter** (YAML)
2. **Author byline** with date
3. **Horizontal rule** (`---`)
4. **`` component** (1-3 sentences)
5. **Horizontal rule** (`---`) (optional)
6. **Main content sections** (H2 with IDs)
7. **Closing section** (Changelog, Thanks, etc.)
---
## Post Type Templates
### Major Release Announcement
```markdown
---
title: "React vX.Y"
author: The React Team
date: YYYY/MM/DD
description: React X.Y is now available on npm! In this post, we'll give an overview of the new features.
---
Month DD, YYYY by [The React Team](/community/team)
---
React vX.Y is now available on npm!
In our [Upgrade Guide](/blog/YYYY/MM/DD/react-xy-upgrade-guide), we shared step-by-step instructions for upgrading. In this post, we'll give an overview of what's new.
- [What's new in React X.Y](#whats-new)
- [Improvements](#improvements)
- [How to upgrade](#how-to-upgrade)
---
## What's new in React X.Y {/*whats-new*/}
### Feature Name {/*feature-name*/}
[Problem this solves. Before/after code examples.]
For more information, see the docs for [`Feature`](/reference/react/Feature).
---
## Improvements in React X.Y {/*improvements*/}
### Improvement Name {/*improvement-name*/}
[Description of improvement.]
---
## How to upgrade {/*how-to-upgrade*/}
See [How to Upgrade to React X.Y](/blog/YYYY/MM/DD/react-xy-upgrade-guide) for step-by-step instructions.
---
## Changelog {/*changelog*/}
### React {/*react*/}
* Add `useNewHook` for [purpose]. ([#12345](https://github.com/facebook/react/pull/12345) by [@contributor](https://github.com/contributor))
---
_Thanks to [Name](url) for reviewing this post._
```
### Upgrade Guide
```markdown
---
title: "React [VERSION] Upgrade Guide"
author: Author Name
date: YYYY/MM/DD
description: Step-by-step instructions for upgrading to React [VERSION].
---
Month DD, YYYY by [Author Name](social-url)
---
[Summary of upgrade and what this guide covers.]
#### Stepping stone version {/*stepping-stone*/}
[If applicable, describe intermediate upgrade steps.]
In this post, we will guide you through the steps for upgrading:
- [Installing](#installing)
- [Codemods](#codemods)
- [Breaking changes](#breaking-changes)
- [New deprecations](#new-deprecations)
---
## Installing {/*installing*/}
```bash
npm install --save-exact react@^X.Y.Z react-dom@^X.Y.Z
```
## Codemods {/*codemods*/}
#### Run all React [VERSION] codemods {/*run-all-codemods*/}
```bash
npx codemod@latest react/[VERSION]/migration-recipe
```
## Breaking changes {/*breaking-changes*/}
### Removed: `apiName` {/*removed-api-name*/}
`apiName` was deprecated in [Month YYYY (vX.X.X)](link).
```js
// Before
[old code]
// After
[new code]
```
Codemod [description]:
```bash
npx codemod@latest react/[VERSION]/codemod-name
```
## New deprecations {/*new-deprecations*/}
### Deprecated: `apiName` {/*deprecated-api-name*/}
[Explanation and migration path.]
---
Thanks to [Contributor](link) for reviewing this post.
```
### React Labs Research Update
```markdown
---
title: "React Labs: What We've Been Working On – [Month Year]"
author: Author1, Author2, and Author3
date: YYYY/MM/DD
description: In React Labs posts, we write about projects in active research and development.
---
Month DD, YYYY by [Author1](url), [Author2](url), and [Author3](url)
---
In React Labs posts, we write about projects in active research and development. We've made significant progress since our [last update](/blog/previous-labs-post), and we'd like to share our progress.
[Optional: Roadmap disclaimer about timelines]
---
## Feature Name {/*feature-name*/}
`` is now available in React's Canary channel.
[Description of feature, motivation, current status.]
### Subsection {/*subsection*/}
[Details, examples, use cases.]
---
## Research Area {/*research-area*/}
[Problem space description. Status communication.]
This research is still early. We'll share more when we're further along.
---
_Thanks to [Reviewer](url) for reviewing this post._
Thanks for reading, and see you in the next update!
```
### React Conf Recap
```markdown
---
title: "React Conf [YEAR] Recap"
author: Author1 and Author2
date: YYYY/MM/DD
description: Last week we hosted React Conf [YEAR]. In this post, we'll summarize the talks and announcements.
---
Month DD, YYYY by [Author1](url) and [Author2](url)
---
Last week we hosted React Conf [YEAR] [where we announced [key announcements]].
---
The entire [day 1](youtube-url) and [day 2](youtube-url) streams are available online.
## Day 1 {/*day-1*/}
_[Watch the full day 1 stream here.](youtube-url)_
[Description of day 1 opening and keynote highlights.]
Watch the full day 1 keynote here:
## Day 2 {/*day-2*/}
_[Watch the full day 2 stream here.](youtube-url)_
[Day 2 summary.]
## Q&A {/*q-and-a*/}
* [Q&A Title](youtube-url) hosted by [Host](url)
## And more... {/*and-more*/}
We also heard talks including:
* [Talk Title](youtube-url) by [Speaker](url)
## Thank you {/*thank-you*/}
Thank you to all the staff, speakers, and participants who made React Conf [YEAR] possible.
See you next time!
```
### Feature/Tool Announcement
```markdown
---
title: "Introducing [Feature Name]"
author: Author Name
date: YYYY/MM/DD
description: Today we are announcing [feature]. In this post, we'll explain [what this post covers].
---
Month DD, YYYY by [Author Name](url)
---
Today we are [excited/thrilled] to announce [feature]. [What this means for users.]
---
## tl;dr {/*tldr*/}
* Key announcement point with [relevant link](/path).
* What users can do now.
* Availability or adoption information.
## What is [Feature]? {/*what-is-feature*/}
[Explanation of the feature/tool.]
## Why we built this {/*why-we-built-this*/}
[Motivation, history, problem being solved.]
## Getting started {/*getting-started*/}
To install [feature]:
npm install package-name
[You can find more documentation here.](/path/to/docs)
## What's next {/*whats-next*/}
[Future plans and next steps.]
## Thank you {/*thank-you*/}
[Acknowledgments to contributors.]
---
Thanks to [Reviewer](url) for reviewing this post.
```
### Security Announcement
```markdown
---
title: "[Severity] Security Vulnerability in [Component]"
author: The React Team
date: YYYY/MM/DD
description: Brief summary of the vulnerability. A fix has been published. We recommend upgrading immediately.
---
Month DD, YYYY by [The React Team](/community/team)
---
[One or two sentences summarizing the vulnerability.]
We recommend upgrading immediately.
---
On [date], [researcher] reported a security vulnerability that allows [description].
This vulnerability was disclosed as [CVE-YYYY-NNNNN](https://www.cve.org/CVERecord?id=CVE-YYYY-NNNNN) and is rated CVSS [score].
The vulnerability is present in versions [list] of:
* [package-name](https://www.npmjs.com/package/package-name)
## Immediate Action Required {/*immediate-action-required*/}
A fix was introduced in versions [linked versions]. Upgrade immediately.
### Affected frameworks {/*affected-frameworks*/}
[List of affected frameworks with npm links.]
### Vulnerability overview {/*vulnerability-overview*/}
[Technical explanation of the vulnerability.]
## Update Instructions {/*update-instructions*/}
### Framework Name {/*update-framework-name*/}
```bash
npm install package@version
```
## Timeline {/*timeline*/}
* **November 29th**: [Researcher] reported the vulnerability.
* **December 1st**: Fix was created and validated.
* **December 3rd**: Fix published and CVE disclosed.
## Attribution {/*attribution*/}
Thank you to [Researcher Name](url) for discovering and reporting this vulnerability.
```
---
## Heading Conventions
### ID Syntax
All headings require IDs using CSS comment syntax:
```markdown
## Heading Text {/*heading-id*/}
```
### ID Rules
- Lowercase
- Kebab-case (hyphens for spaces)
- Remove special characters (apostrophes, colons, backticks)
- Concise but descriptive
### Heading Patterns
| Context | Example |
|---------|---------|
| Feature section | `## New Feature: Automatic Batching {/*new-feature-automatic-batching*/}` |
| New hook | `### New hook: \`useActionState\` {/*new-hook-useactionstate*/}` |
| API in backticks | `### \`\` {/*activity*/}` |
| Removed API | `#### Removed: \`propTypes\` {/*removed-proptypes*/}` |
| tl;dr section | `## tl;dr {/*tldr*/}` |
---
## Component Usage Guide
### Blog-Appropriate Components
| Component | Usage in Blog |
|-----------|---------------|
| `` | **Required** - Opening summary after byline |
| `` | Callouts, caveats, important clarifications |
| `` | Warnings about common mistakes |
| `` | Optional technical deep dives (use sparingly) |
| `` | CLI/installation commands |
| `` | Console error/warning output |
| `` | Multi-line console output |
| `` | Conference video embeds |
| `` | Visual explanations |
| `` | Auto-generated table of contents |
### `` Pattern
Always wrap opening paragraph:
```markdown
React 19 is now available on npm!
```
### `` Patterns
**Simple note:**
```markdown
For React Native users, React 18 ships with the New Architecture.
```
**Titled note (H4 inside):**
```markdown
#### React 18.3 has also been published {/*react-18-3*/}
To help with the upgrade, we've published `react@18.3`...
```
### `` Pattern
```markdown
npm install react@latest react-dom@latest
```
### `` Pattern
```markdown
```
---
## Link Patterns
### Internal Links
| Type | Pattern | Example |
|------|---------|---------|
| Blog post | `/blog/YYYY/MM/DD/slug` | `/blog/2024/12/05/react-19` |
| API reference | `/reference/react/HookName` | `/reference/react/useState` |
| Learn section | `/learn/topic-name` | `/learn/react-compiler` |
| Community | `/community/team` | `/community/team` |
### External Links
| Type | Pattern |
|------|---------|
| GitHub PR | `[#12345](https://github.com/facebook/react/pull/12345)` |
| GitHub user | `[@username](https://github.com/username)` |
| Twitter/X | `[@username](https://x.com/username)` |
| Bluesky | `[Name](https://bsky.app/profile/handle)` |
| CVE | `[CVE-YYYY-NNNNN](https://www.cve.org/CVERecord?id=CVE-YYYY-NNNNN)` |
| npm package | `[package](https://www.npmjs.com/package/package)` |
### "See docs" Pattern
```markdown
For more information, see the docs for [`useActionState`](/reference/react/useActionState).
```
---
## Changelog Format
### Bullet Pattern
```markdown
* Add `useTransition` for concurrent rendering. ([#10426](https://github.com/facebook/react/pull/10426) by [@acdlite](https://github.com/acdlite))
* Fix `useReducer` observing incorrect props. ([#22445](https://github.com/facebook/react/pull/22445) by [@josephsavona](https://github.com/josephsavona))
```
**Structure:** `Verb` + backticked API + description + `([#PR](url) by [@user](url))`
**Verbs:** Add, Fix, Remove, Make, Improve, Allow, Deprecate
### Section Organization
```markdown
## Changelog {/*changelog*/}
### React {/*react*/}
* [changes]
### React DOM {/*react-dom*/}
* [changes]
```
---
## Acknowledgments Format
### Post-closing Thanks
```markdown
---
Thanks to [Name](url), [Name](url), and [Name](url) for reviewing this post.
```
Or italicized:
```markdown
_Thanks to [Name](url) for reviewing this post._
```
### Update Notes
For post-publication updates:
```markdown
[Updated content]
-----
_Updated January 26, 2026._
```
---
## Tone & Length by Post Type
| Type | Tone | Length | Key Elements |
|------|------|--------|--------------|
| Release | Celebratory, informative | Medium-long | Feature overview, upgrade link, changelog |
| Upgrade | Instructional, precise | Long | Step-by-step, codemods, breaking changes |
| Labs | Transparent, exploratory | Medium | Status updates, roadmap disclaimers |
| Conf | Enthusiastic, community-focused | Medium | YouTube embeds, speaker credits |
| Feature | Excited, explanatory | Medium | tl;dr, "why", getting started |
| Security | Urgent, factual | Short-medium | Immediate action, timeline, CVE |
---
## Do's and Don'ts
**Do:**
- Focus on facts over marketing
- Say "upcoming" explicitly for unreleased features
- Include FAQ sections for major announcements
- Credit contributors and link to GitHub
- Use "we" voice for team posts
- Link to upgrade guides from release posts
- Include table of contents for long posts
- End with acknowledgments
**Don't:**
- Promise features not yet available
- Rewrite history (add update notes instead)
- Break existing URLs
- Use hyperbolic language ("revolutionary", "game-changing")
- Skip the `` component
- Forget heading IDs
- Use heavy component nesting in blogs
- Make time estimates or predictions
---
## Updating Old Posts
- Never break existing URLs; add redirects when URLs change
- Don't rewrite history; add update notes instead:
```markdown
[Updated information]
-----
_Updated Month Year._
```
---
## Critical Rules
1. **Heading IDs required:** `## Title {/*title-id*/}`
2. **`` required:** Every post starts with `` component
3. **Byline required:** Date + linked author(s) after frontmatter
4. **Date format:** Frontmatter uses `YYYY/MM/DD`, byline uses `Month DD, YYYY`
5. **Link to docs:** New APIs must link to reference documentation
6. **Security posts:** Always include "We recommend upgrading immediately"
---
## Components Reference
For complete MDX component patterns, invoke `/docs-components`.
Blog posts commonly use: ``, ``, ``, ``, ``, ``, ``, ``.
Prefer inline explanations over heavy component usage.
================================================
FILE: .claude/skills/docs-writer-learn/SKILL.md
================================================
---
name: docs-writer-learn
description: Use when writing or editing files in src/content/learn/. Provides Learn page structure and tone.
---
# Learn Page Writer
## Persona
**Voice:** Patient teacher guiding a friend through concepts
**Tone:** Conversational, warm, encouraging
## Voice & Style
For tone, capitalization, jargon, and prose patterns, invoke `/docs-voice`.
## Page Structure Variants
### 1. Standard Learn Page (Most Common)
```mdx
---
title: Page Title
---
1-3 sentences introducing the concept. Use *italics* for new terms.
* Learning outcome 1
* Learning outcome 2
* Learning outcome 3-5
## Section Name {/*section-id*/}
Content with Sandpack examples, Pitfalls, Notes, DeepDives...
## Another Section {/*another-section*/}
More content...
* Summary point 1
* Summary point 2
* Summary points 3-9
#### Challenge title {/*challenge-id*/}
Description...
Optional guidance (single paragraph)
{/* Starting code */}
Explanation...
{/* Fixed code */}
```
### 2. Chapter Introduction Page
For pages that introduce a chapter (like describing-the-ui.md, managing-state.md):
```mdx
* [Sub-page title](/learn/sub-page-name) to learn...
* [Another page](/learn/another-page) to learn...
## Preview Section {/*section-id*/}
Preview description with mini Sandpack example
Read **[Page Title](/learn/sub-page-name)** to learn how to...
## What's next? {/*whats-next*/}
Head over to [First Page](/learn/first-page) to start reading this chapter page by page!
```
**Important:** Chapter intro pages do NOT include `` or `` sections.
### 3. Tutorial Page
For step-by-step tutorials (like tutorial-tic-tac-toe.md):
```mdx
Brief statement of what will be built
Alternative learning path offered
Table of contents (prose listing of major sections)
## Setup {/*setup*/}
...
## Main Content {/*main-content*/}
Progressive code building with ### subsections
No YouWillLearn, Recap, or Challenges
Ends with ordered list of "extra credit" improvements
```
### 4. Reference-Style Learn Page
For pages with heavy API documentation (like typescript.md):
```mdx
* [Link to section](#section-anchor)
* [Link to another section](#another-section)
## Sections with ### subsections
## Further learning {/*further-learning*/}
No Recap or Challenges
```
## Heading ID Conventions
All headings require IDs in `{/*kebab-case*/}` format:
```markdown
## Section Title {/*section-title*/}
### Subsection Title {/*subsection-title*/}
#### DeepDive Title {/*deepdive-title*/}
```
**ID Generation Rules:**
- Lowercase everything
- Replace spaces with hyphens
- Remove apostrophes, quotes
- Remove or convert special chars (`:`, `?`, `!`, `.`, parentheses)
**Examples:**
- "What's React?" → `{/*whats-react*/}`
- "Step 1: Create the context" → `{/*step-1-create-the-context*/}`
- "Conditional (ternary) operator (? :)" → `{/*conditional-ternary-operator--*/}`
## Teaching Patterns
### Problem-First Teaching
Show broken/problematic code BEFORE the solution:
1. Present problematic approach with `// 🔴 Avoid:` comment
2. Explain WHY it's wrong (don't just say it is)
3. Show the solution with `// ✅ Good:` comment
4. Invite experimentation
### Progressive Complexity
Build understanding in layers:
1. Show simplest working version
2. Identify limitation or repetition
3. Introduce solution incrementally
4. Show complete solution
5. Invite experimentation: "Try changing..."
### Numbered Step Patterns
For multi-step processes:
**As section headings:**
```markdown
### Step 1: Action to take {/*step-1-action*/}
### Step 2: Next action {/*step-2-next-action*/}
```
**As inline lists:**
```markdown
To implement this:
1. **Declare** `inputRef` with the `useRef` Hook.
2. **Pass it** as ``.
3. **Read** the input DOM node from `inputRef.current`.
```
### Interactive Invitations
After Sandpack examples, encourage experimentation:
- "Try changing X to Y. See how...?"
- "Try it in the sandbox above!"
- "Click each button separately:"
- "Have a guess!"
- "Verify that..."
### Decision Questions
Help readers build intuition:
> "When you're not sure whether some code should be in an Effect or in an event handler, ask yourself *why* this code needs to run."
## Component Placement Order
1. `` - First after frontmatter
2. `` - After Intro (standard/chapter pages)
3. Body content with ``, ``, `` placed contextually
4. `` - Before Challenges (standard pages only)
5. `` - End of page (standard pages only)
For component structure and syntax, invoke `/docs-components`.
## Code Examples
For Sandpack file structure, naming conventions, code style, and pedagogical markers, invoke `/docs-sandpack`.
## Cross-Referencing
### When to Link
**Link to /learn:**
- Explaining concepts or mental models
- Teaching how things work together
- Tutorials and guides
- "Why" questions
**Link to /reference:**
- API details, Hook signatures
- Parameter lists and return values
- Rules and restrictions
- "What exactly" questions
### Link Formats
```markdown
[concept name](/learn/page-name)
[`useState`](/reference/react/useState)
[section link](/learn/page-name#section-id)
[MDN](https://developer.mozilla.org/...)
```
## Section Dividers
**Important:** Learn pages typically do NOT use `---` dividers. The heading hierarchy provides sufficient structure. Only consider dividers in exceptional cases like separating main content from meta/contribution sections.
## Do's and Don'ts
**Do:**
- Use "you" to address the reader
- Show broken code before fixes
- Explain behavior before naming concepts
- Build concepts progressively
- Include interactive Sandpack examples
- Use established analogies consistently
- Place Pitfalls AFTER explaining concepts
- Invite experimentation with "Try..." phrases
**Don't:**
- Use "simple", "easy", "just", or time estimates
- Reference concepts not yet introduced
- Skip required components for page type
- Use passive voice without reason
- Place Pitfalls before teaching the concept
- Use `---` dividers between sections
- Create unnecessary abstraction in examples
- Place consecutive Pitfalls or Notes without separating prose (combine or separate)
## Critical Rules
1. **All headings require IDs:** `## Title {/*title-id*/}`
2. **Chapter intros use `isChapter={true}` and ``**
3. **Tutorial pages omit YouWillLearn/Recap/Challenges**
4. **Problem-first teaching:** Show broken → explain → fix
5. **No consecutive Pitfalls/Notes:** See `/docs-components` Callout Spacing Rules
For component patterns, invoke `/docs-components`. For Sandpack patterns, invoke `/docs-sandpack`.
================================================
FILE: .claude/skills/docs-writer-reference/SKILL.md
================================================
---
name: docs-writer-reference
description: Reference page structure, templates, and writing patterns for src/content/reference/. For components, see /docs-components. For code examples, see /docs-sandpack.
---
# Reference Page Writer
## Quick Reference
### Page Type Decision Tree
1. Is it a Hook? Use **Type A (Hook/Function)**
2. Is it a React component (``)? Use **Type B (Component)**
3. Is it a compiler configuration option? Use **Type C (Configuration)**
4. Is it a directive (`'use something'`)? Use **Type D (Directive)**
5. Is it an ESLint rule? Use **Type E (ESLint Rule)**
6. Is it listing multiple APIs? Use **Type F (Index/Category)**
### Component Selection
For component selection and patterns, invoke `/docs-components`.
---
## Voice & Style
**Voice:** Authoritative technical reference writer
**Tone:** Precise, comprehensive, neutral
For tone, capitalization, jargon, and prose patterns, invoke `/docs-voice`.
**Do:**
- Start with single-line description: "`useState` is a React Hook that lets you..."
- Include Parameters, Returns, Caveats sections for every API
- Document edge cases most developers will encounter
- Use section dividers between major sections
- Include "See more examples below" links
- Be assertive, not hedging - "This is designed for..." not "This helps avoid issues with..."
- State facts, not benefits - "The callback always accesses the latest values" not "This helps avoid stale closures"
- Use minimal but meaningful names - `onEvent` or `onTick` over `onSomething`
**Don't:**
- Skip the InlineToc component
- Omit error cases or caveats
- Use conversational language
- Mix teaching with reference (that's Learn's job)
- Document past bugs or fixed issues
- Include niche edge cases (e.g., `this` binding, rare class patterns)
- Add phrases explaining "why you'd want this" - the Usage section examples do that
- Exception: Pitfall and DeepDive asides can use slightly conversational phrasing
---
## Page Templates
### Type A: Hook/Function
**When to use:** Documenting React hooks and standalone functions (useState, useEffect, memo, lazy, etc.)
```mdx
---
title: hookName
---
`hookName` is a React Hook that lets you [brief description].
```js
const result = hookName(arg)
```
---
## Reference {/*reference*/}
### `hookName(arg)` {/*hookname*/}
Call `hookName` at the top level of your component to...
```js
[signature example with annotations]
```
[See more examples below.](#usage)
#### Parameters {/*parameters*/}
* `arg`: Description of the parameter.
#### Returns {/*returns*/}
Description of return value.
#### Caveats {/*caveats*/}
* Important caveat about usage.
---
## Usage {/*usage*/}
### Common Use Case {/*common-use-case*/}
Explanation with Sandpack examples...
---
## Troubleshooting {/*troubleshooting*/}
### Common Problem {/*common-problem*/}
How to solve it...
```
---
### Type B: Component
**When to use:** Documenting React components (Suspense, Fragment, Activity, StrictMode)
```mdx
---
title:
---
`` lets you [primary action].
```js
```
---
## Reference {/*reference*/}
### `` {/*componentname*/}
[Component purpose and behavior]
#### Props {/*props*/}
* `propName`: Description of the prop...
* **optional** `optionalProp`: Description...
#### Caveats {/*caveats*/}
* [Caveats specific to this component]
```
**Key differences from Hook pages:**
- Title uses JSX syntax: ``
- Uses `#### Props` instead of `#### Parameters`
- Reference heading uses JSX: `` ### `` ``
---
### Type C: Configuration
**When to use:** Documenting React Compiler configuration options
```mdx
---
title: optionName
---
The `optionName` option [controls/specifies/determines] [what it does].
```js
{
optionName: 'value' // Quick example
}
```
---
## Reference {/*reference*/}
### `optionName` {/*optionname*/}
[Description of the option's purpose]
#### Type {/*type*/}
```
'value1' | 'value2' | 'value3'
```
#### Default value {/*default-value*/}
`'value1'`
#### Options {/*options*/}
- **`'value1'`** (default): Description
- **`'value2'`**: Description
- **`'value3'`**: Description
#### Caveats {/*caveats*/}
* [Usage caveats]
```
---
### Type D: Directive
**When to use:** Documenting directives like 'use server', 'use client', 'use memo'
```mdx
---
title: "'use directive'"
titleForTitleTag: "'use directive' directive"
---
`'use directive'` is for use with [React Server Components](/reference/rsc/server-components).
`'use directive'` marks [what it marks] for [purpose].
```js {1}
function MyComponent() {
'use directive';
// ...
}
```
---
## Reference {/*reference*/}
### `'use directive'` {/*use-directive*/}
Add `'use directive'` at the beginning of [location] to [action].
#### Caveats {/*caveats*/}
* `'use directive'` must be at the very beginning...
* The directive must be written with single or double quotes, not backticks.
* [Other placement/syntax caveats]
```
**Key characteristics:**
- Title includes quotes: `title: "'use server'"`
- Uses `titleForTitleTag` for browser tab title
- `` block appears before ``
- Caveats focus on placement and syntax requirements
---
### Type E: ESLint Rule
**When to use:** Documenting ESLint plugin rules
```mdx
---
title: rule-name
---
Validates that [what the rule checks].
## Rule Details {/*rule-details*/}
[Explanation of why this rule exists and React's underlying assumptions]
## Common Violations {/*common-violations*/}
[Description of violation patterns]
### Invalid {/*invalid*/}
Examples of incorrect code for this rule:
```js
// X Missing dependency
useEffect(() => {
console.log(count);
}, []); // Missing 'count'
```
### Valid {/*valid*/}
Examples of correct code for this rule:
```js
// checkmark All dependencies included
useEffect(() => {
console.log(count);
}, [count]);
```
## Troubleshooting {/*troubleshooting*/}
### [Problem description] {/*problem-slug*/}
[Solution]
## Options {/*options*/}
[Configuration options if applicable]
```
**Key characteristics:**
- Intro is a single "Validates that..." sentence
- Uses "Invalid"/"Valid" sections with emoji-prefixed code comments
- Rule Details explains "why" not just "what"
---
### Type F: Index/Category
**When to use:** Overview pages listing multiple APIs in a category
```mdx
---
title: "Built-in React [Type]"
---
*Concept* let you [purpose]. Brief scope statement.
---
## Category Name {/*category-name*/}
*Concept* explanation with [Learn section link](/learn/topic).
To [action], use one of these [Type]:
* [`apiName`](/reference/react/apiName) lets you [action].
* [`apiName`](/reference/react/apiName) declares [thing].
```js
function Example() {
const value = useHookName(args);
}
```
---
## Your own [Type] {/*your-own-type*/}
You can also [define your own](/learn/topic) as JavaScript functions.
```
**Key characteristics:**
- Title format: "Built-in React [Type]"
- Italicized concept definitions
- Horizontal rules between sections
- Closes with "Your own [Type]" section
---
## Advanced Patterns
### Multi-Function Documentation
**When to use:** When a hook returns a function that needs its own documentation (useState's setter, useReducer's dispatch)
```md
### `hookName(args)` {/*hookname*/}
[Main hook documentation]
#### Parameters {/*parameters*/}
#### Returns {/*returns*/}
#### Caveats {/*caveats*/}
---
### `set` functions, like `setSomething(nextState)` {/*setstate*/}
The `set` function returned by `hookName` lets you [action].
#### Parameters {/*setstate-parameters*/}
#### Returns {/*setstate-returns*/}
#### Caveats {/*setstate-caveats*/}
```
**Key conventions:**
- Horizontal rule (`---`) separates main hook from returned function
- Heading IDs include prefix: `{/*setstate-parameters*/}` vs `{/*parameters*/}`
- Use generic names: "set functions" not "setCount"
---
### Compound Return Objects
**When to use:** When a function returns an object with multiple properties/methods (createContext)
```md
### `createContext(defaultValue)` {/*createcontext*/}
[Main function documentation]
#### Returns {/*returns*/}
`createContext` returns a context object.
**The context object itself does not hold any information.** It represents...
* `SomeContext` lets you provide the context value.
* `SomeContext.Consumer` is an alternative way to read context.
---
### `SomeContext` Provider {/*provider*/}
[Documentation for Provider]
#### Props {/*provider-props*/}
---
### `SomeContext.Consumer` {/*consumer*/}
[Documentation for Consumer]
#### Props {/*consumer-props*/}
```
---
## Writing Patterns
### Opening Lines by Page Type
| Page Type | Pattern | Example |
|-----------|---------|---------|
| Hook | `` `hookName` is a React Hook that lets you [action]. `` | "`useState` is a React Hook that lets you add a state variable to your component." |
| Component | `` `` lets you [action]. `` | "`` lets you display a fallback until its children have finished loading." |
| API | `` `apiName` lets you [action]. `` | "`memo` lets you skip re-rendering a component when its props are unchanged." |
| Configuration | `` The `optionName` option [controls/specifies/determines] [what]. `` | "The `target` option specifies which React version the compiler generates code for." |
| Directive | `` `'directive'` [marks/opts/prevents] [what] for [purpose]. `` | "`'use server'` marks a function as callable from the client." |
| ESLint Rule | `` Validates that [condition]. `` | "Validates that dependency arrays for React hooks contain all necessary dependencies." |
---
### Parameter Patterns
**Simple parameter:**
```md
* `paramName`: Description of what it does.
```
**Optional parameter:**
```md
* **optional** `paramName`: Description of what it does.
```
**Parameter with special function behavior:**
```md
* `initialState`: The value you want the state to be initially. It can be a value of any type, but there is a special behavior for functions. This argument is ignored after the initial render.
* If you pass a function as `initialState`, it will be treated as an _initializer function_. It should be pure, should take no arguments, and should return a value of any type.
```
**Callback parameter with sub-parameters:**
```md
* `subscribe`: A function that takes a single `callback` argument and subscribes it to the store. When the store changes, it should invoke the provided `callback`. The `subscribe` function should return a function that cleans up the subscription.
```
**Nested options object:**
```md
* **optional** `options`: An object with options for this React root.
* **optional** `onCaughtError`: Callback called when React catches an error in an Error Boundary.
* **optional** `onUncaughtError`: Callback called when an error is thrown and not caught.
* **optional** `identifierPrefix`: A string prefix React uses for IDs generated by `useId`.
```
---
### Return Value Patterns
**Single value return:**
```md
`hookName` returns the current value. The value will be the same as `initialValue` during the first render.
```
**Array return (numbered list):**
```md
`useState` returns an array with exactly two values:
1. The current state. During the first render, it will match the `initialState` you have passed.
2. The [`set` function](#setstate) that lets you update the state to a different value and trigger a re-render.
```
**Object return (bulleted list):**
```md
`createElement` returns a React element object with a few properties:
* `type`: The `type` you have passed.
* `props`: The `props` you have passed except for `ref` and `key`.
* `ref`: The `ref` you have passed. If missing, `null`.
* `key`: The `key` you have passed, coerced to a string. If missing, `null`.
```
**Promise return:**
```md
`prerender` returns a Promise:
- If rendering is successful, the Promise will resolve to an object containing:
- `prelude`: a [Web Stream](MDN-link) of HTML.
- `postponed`: a JSON-serializable object for resumption.
- If rendering fails, the Promise will be rejected.
```
**Wrapped function return:**
```md
`cache` returns a cached version of `fn` with the same type signature. It does not call `fn` in the process.
When calling `cachedFn` with given arguments, it first checks if a cached result exists. If cached, it returns the result. If not, it calls `fn`, stores the result, and returns it.
```
---
### Caveats Patterns
**Standard Hook caveat (almost always first for Hooks):**
```md
* `useXxx` is a Hook, so you can only call it **at the top level of your component** or your own Hooks. You can't call it inside loops or conditions. If you need that, extract a new component and move the state into it.
```
**Stable identity caveat (for returned functions):**
```md
* The `set` function has a stable identity, so you will often see it omitted from Effect dependencies, but including it will not cause the Effect to fire.
```
**Strict Mode caveat:**
```md
* In Strict Mode, React will **call your render function twice** in order to help you find accidental impurities. This is development-only behavior and does not affect production.
```
**Caveat with code example:**
```md
* It's not recommended to _suspend_ a render based on a store value returned by `useSyncExternalStore`. For example, the following is discouraged:
```js
const selectedProductId = useSyncExternalStore(...);
const data = use(fetchItem(selectedProductId)) // X Don't suspend based on store value
```
```
**Canary caveat:**
```md
* If you want to pass `ref` to a Fragment, you can't use the `<>...>` syntax.
```
---
### Troubleshooting Patterns
**Heading format (first person problem statements):**
```md
### I've updated the state, but logging gives me the old value {/*old-value*/}
### My initializer or updater function runs twice {/*runs-twice*/}
### I want to read the latest state from a callback {/*read-latest-state*/}
```
**Error message format:**
```md
### I'm getting an error: "Too many re-renders" {/*too-many-rerenders*/}
### I'm getting an error: "Rendered more hooks than during the previous render" {/*more-hooks*/}
```
**Lint error format:**
```md
### I'm getting a lint error: "[exact error message]" {/*lint-error-slug*/}
```
**Problem-solution structure:**
1. State the problem with code showing the issue
2. Explain why it happens
3. Provide the solution with corrected code
4. Link to Learn section for deeper understanding
---
### Code Comment Conventions
For code comment conventions (wrong/right, legacy/recommended, server/client labeling, bundle size annotations), invoke `/docs-sandpack`.
---
### Link Description Patterns
| Pattern | Example |
|---------|---------|
| "lets you" + action | "`memo` lets you skip re-rendering when props are unchanged." |
| "declares" + thing | "`useState` declares a state variable that you can update directly." |
| "reads" + thing | "`useContext` reads and subscribes to a context." |
| "connects" + thing | "`useEffect` connects a component to an external system." |
| "Used with" | "Used with [`useContext`.](/reference/react/useContext)" |
| "Similar to" | "Similar to [`useTransition`.](/reference/react/useTransition)" |
---
## Component Patterns
For comprehensive MDX component patterns (Note, Pitfall, DeepDive, Recipes, Deprecated, RSC, Canary, Diagram, Code Steps), invoke `/docs-components`.
For Sandpack-specific patterns and code style, invoke `/docs-sandpack`.
### Reference-Specific Component Rules
**Component placement in Reference pages:**
- `` goes before `` at top of page
- `` goes after `` for page-level deprecation
- `` goes after method heading for method-level deprecation
- `` wrapper goes inline within ``
- `` appears in headings, props lists, and caveats
**Troubleshooting-specific components:**
- Use first-person problem headings
- Cross-reference Pitfall IDs when relevant
**Callout spacing:**
- Never place consecutive Pitfalls or consecutive Notes
- Combine related warnings into one with titled subsections, or separate with prose/code
- Consecutive DeepDives OK for multi-part explorations
- See `/docs-components` Callout Spacing Rules
---
## Content Principles
### Intro Section
- **One sentence, ~15 words max** - State what the Hook does, not how it works
- ✅ "`useEffectEvent` is a React Hook that lets you separate events from Effects."
- ❌ "`useEffectEvent` is a React Hook that lets you extract non-reactive logic from your Effects into a reusable function called an Effect Event."
### Reference Code Example
- Show just the API call (5-10 lines), not a full component
- Move full component examples to Usage section
### Usage Section Structure
1. **First example: Core mental model** - Show the canonical use case with simplest concrete example
2. **Subsequent examples: Canonical use cases** - Name the *why* (e.g., "Avoid reconnecting to external systems"), show a concrete *how*
- Prefer broad canonical use cases over multiple narrow concrete examples
- The section title IS the teaching - "When would I use this?" should be answered by the heading
### What to Include vs. Exclude
- **Never** document past bugs or fixed issues
- **Include** edge cases most developers will encounter
- **Exclude** niche edge cases (e.g., `this` binding, rare class patterns)
### Caveats Section
- Include rules the linter enforces or that cause immediate errors
- Include fundamental usage restrictions
- Exclude implementation details unless they affect usage
- Exclude repetition of things explained elsewhere
- Keep each caveat to one sentence when possible
### Troubleshooting Section
- Error headings only: "I'm getting an error: '[message]'" format
- Never document past bugs - if it's fixed, it doesn't belong here
- Focus on errors developers will actually encounter today
### DeepDive Content
- **Goldilocks principle** - Deep enough for curious developers, short enough to not overwhelm
- Answer "why is it designed this way?" - not exhaustive technical details
- Readers who skip it should miss nothing essential for using the API
- If the explanation is getting long, you're probably explaining too much
---
## Domain-Specific Guidance
### Hooks
**Returned function documentation:**
- Document setter/dispatch functions as separate `###` sections
- Use generic names: "set functions" not "setCount"
- Include stable identity caveat for returned functions
**Dependency array documentation:**
- List what counts as reactive values
- Explain when dependencies are ignored
- Link to removing effect dependencies guide
**Recipes usage:**
- Group related examples with meaningful titleText
- Each recipe has brief intro, Sandpack, and ``
---
### Components
**Props documentation:**
- Use `#### Props` instead of `#### Parameters`
- Mark optional props with `**optional**` prefix
- Use `` inline for canary-only props
**JSX syntax in titles/headings:**
- Frontmatter title: `title: `
- Reference heading: `` ### `` {/*suspense*/} ``
---
### React-DOM
**Common props linking:**
```md
`` supports all [common element props.](/reference/react-dom/components/common#common-props)
```
**Props categorization:**
- Controlled vs uncontrolled props grouped separately
- Form-specific props documented with action patterns
- MDN links for standard HTML attributes
**Environment-specific notes:**
```mdx
This API is specific to Node.js. Environments with [Web Streams](MDN-link), like Deno and modern edge runtimes, should use [`renderToReadableStream`](/reference/react-dom/server/renderToReadableStream) instead.
```
**Progressive enhancement:**
- Document benefits for users without JavaScript
- Explain Server Function + form action integration
- Show hidden form field and `.bind()` patterns
---
### RSC
**RSC banner (before Intro):**
Always place `` component before `` for Server Component-only APIs.
**Serialization type lists:**
When documenting Server Function arguments, list supported types:
```md
Supported types for Server Function arguments:
* Primitives
* [string](MDN-link)
* [number](MDN-link)
* Iterables containing serializable values
* [Array](MDN-link)
* [Map](MDN-link)
Notably, these are not supported:
* React elements, or [JSX](/learn/writing-markup-with-jsx)
* Functions (other than Server Functions)
```
**Bundle size comparisons:**
- Show "Not included in bundle" for server-only imports
- Annotate client bundle sizes with gzip: `// 35.9K (11.2K gzipped)`
---
### Compiler
**Configuration page structure:**
- Type (union type or interface)
- Default value
- Options/Valid values with descriptions
**Directive documentation:**
- Placement requirements are critical
- Mode interaction tables showing combinations
- "Use sparingly" + "Plan for removal" patterns for escape hatches
**Library author guides:**
- Audience-first intro
- Benefits/Why section
- Numbered step-by-step setup
---
### ESLint
**Rule Details section:**
- Explain "why" not just "what"
- Focus on React's underlying assumptions
- Describe consequences of violations
**Invalid/Valid sections:**
- Standard intro: "Examples of [in]correct code for this rule:"
- Use X emoji for invalid, checkmark for valid
- Show inline comments explaining the violation
**Configuration options:**
- Show shared settings (preferred)
- Show rule-level options (backward compatibility)
- Note precedence when both exist
---
## Edge Cases
For deprecated, canary, and version-specific component patterns (placement, syntax, examples), invoke `/docs-components`.
**Quick placement rules:**
- `` after `` for page-level, after heading for method-level
- `` wrapper inline in Intro, `` in headings/props/caveats
- Version notes use `` with "Starting in React 19..." pattern
**Removed APIs on index pages:**
```md
## Removed APIs {/*removed-apis*/}
These APIs were removed in React 19:
* [`render`](https://18.react.dev/reference/react-dom/render): use [`createRoot`](/reference/react-dom/client/createRoot) instead.
```
Link to previous version docs (18.react.dev) for removed API documentation.
---
## Critical Rules
1. **Heading IDs required:** `## Title {/*title-id*/}` (lowercase, hyphens)
2. **Sandpack main file needs `export default`**
3. **Active file syntax:** ` ```js src/File.js active `
4. **Error headings in Troubleshooting:** Use `### I'm getting an error: "[message]" {/*id*/}`
5. **Section dividers (`---`)** required between headings (see Section Dividers below)
6. **InlineToc required:** Always include `` after Intro
7. **Consistent parameter format:** Use `* \`paramName\`: description` with `**optional**` prefix for optional params
8. **Numbered lists for array returns:** When hooks return arrays, use numbered lists in Returns section
9. **Generic names for returned functions:** Use "set functions" not "setCount"
10. **Props vs Parameters:** Use `#### Props` for Components (Type B), `#### Parameters` for Hooks/APIs (Type A)
11. **RSC placement:** `` component goes before ``, not after
12. **Canary markers:** Use `` wrapper inline in Intro, `` in headings/props
13. **Deprecated placement:** `` goes after `` for page-level, after heading for method-level
14. **Code comment emojis:** Use X for wrong, checkmark for correct in code examples
15. **No consecutive Pitfalls/Notes:** Combine into one component with titled subsections, or separate with prose/code (see `/docs-components`)
For component heading level conventions (DeepDive, Pitfall, Note, Recipe headings), see `/docs-components`.
### Section Dividers
Use `---` horizontal rules to visually separate major sections:
- **After ``** - Before `## Reference` heading
- **Between API subsections** - Between different function/hook definitions (e.g., between `useState()` and `set functions`)
- **Before `## Usage`** - Separates API reference from examples
- **Before `## Troubleshooting`** - Separates content from troubleshooting
- **Between EVERY Usage subsections** - When switching to a new major use case
Always have a blank line before and after `---`.
### Section ID Conventions
| Section | ID Format |
|---------|-----------|
| Main function | `{/*functionname*/}` |
| Returned function | `{/*setstate*/}`, `{/*dispatch*/}` |
| Sub-section of returned function | `{/*setstate-parameters*/}` |
| Troubleshooting item | `{/*problem-description-slug*/}` |
| Pitfall | `{/*pitfall-description*/}` |
| Deep dive | `{/*deep-dive-topic*/}` |
================================================
FILE: .claude/skills/react-expert/SKILL.md
================================================
---
name: react-expert
description: Use when researching React APIs or concepts for documentation. Use when you need authoritative usage examples, caveats, warnings, or errors for a React feature.
---
# React Expert Research Skill
## Overview
This skill produces exhaustive documentation research on any React API or concept by searching authoritative sources (tests, source code, PRs, issues) rather than relying on LLM training knowledge.
**Skepticism Mandate:** You must be skeptical of your own knowledge. Claude is often trained on outdated or incorrect React patterns. Treat source material as the sole authority. If findings contradict your prior understanding, explicitly flag this discrepancy.
**Red Flags - STOP if you catch yourself thinking:**
- "I know this API does X" → Find source evidence first
- "Common pattern is Y" → Verify in test files
- Generating example code → Must have source file reference
## Invocation
```
/react-expert useTransition
/react-expert suspense boundaries
/react-expert startTransition
```
## Sources (Priority Order)
1. **React Repo Tests** - Most authoritative for actual behavior
2. **React Source Code** - Warnings, errors, implementation details
3. **Git History** - Commit messages with context
4. **GitHub PRs & Comments** - Design rationale (via `gh` CLI)
5. **GitHub Issues** - Confusion/questions (facebook/react + reactjs/react.dev)
6. **React Working Group** - Design discussions for newer APIs
7. **Flow Types** - Source of truth for type signatures
8. **TypeScript Types** - Note discrepancies with Flow
9. **Current react.dev docs** - Baseline (not trusted as complete)
**No web search** - No Stack Overflow, blog posts, or web searches. GitHub API via `gh` CLI is allowed.
## Workflow
### Step 1: Setup React Repo
First, ensure the React repo is available locally:
```bash
# Check if React repo exists, clone or update
if [ -d ".claude/react" ]; then
cd .claude/react && git pull origin main
else
git clone --depth=100 https://github.com/facebook/react.git .claude/react
fi
```
Get the current commit hash for the research document:
```bash
cd .claude/react && git rev-parse --short HEAD
```
### Step 2: Dispatch 6 Parallel Research Agents
Spawn these agents IN PARALLEL using the Task tool. Each agent receives the skepticism preamble:
> "You are researching React's ``. CRITICAL: Do NOT rely on your prior knowledge about this API. Your training may contain outdated or incorrect patterns. Only report what you find in the source files. If your findings contradict common understanding, explicitly highlight this discrepancy."
| Agent | subagent_type | Focus | Instructions |
|-------|---------------|-------|--------------|
| test-explorer | Explore | Test files for usage patterns | Search `.claude/react/packages/*/src/__tests__/` for test files mentioning the topic. Extract actual usage examples WITH file paths and line numbers. |
| source-explorer | Explore | Warnings/errors in source | Search `.claude/react/packages/*/src/` for console.error, console.warn, and error messages mentioning the topic. Document trigger conditions. |
| git-historian | Explore | Commit messages | Run `git log --all --grep="" --oneline -50` in `.claude/react`. Read full commit messages for context. |
| pr-researcher | Explore | PRs introducing/modifying API | Run `gh pr list -R facebook/react --search "" --state all --limit 20`. Read key PR descriptions and comments. |
| issue-hunter | Explore | Issues showing confusion | Search issues in both `facebook/react` and `reactjs/react.dev` repos. Look for common questions and misunderstandings. |
| types-inspector | Explore | Flow + TypeScript signatures | Find Flow types in `.claude/react/packages/*/src/*.js` (look for `@flow` annotations). Find TS types in `.claude/react/packages/*/index.d.ts`. Note discrepancies. |
### Step 3: Agent Prompts
Use these exact prompts when spawning agents:
#### test-explorer
```
You are researching React's .
CRITICAL: Do NOT rely on your prior knowledge about this API. Your training may contain outdated or incorrect patterns. Only report what you find in the source files.
Your task: Find test files in .claude/react that demonstrate usage.
1. Search for test files: Glob for `**/__tests__/**/**` and `**/__tests__/**/*.js` then grep for
2. For each relevant test file, extract:
- The test description (describe/it blocks)
- The actual usage code
- Any assertions about behavior
- Edge cases being tested
3. Report findings with exact file paths and line numbers
Format your output as:
## Test File:
### Test: ""
```javascript
```
**Behavior:**
```
#### source-explorer
```
You are researching React's .
CRITICAL: Do NOT rely on your prior knowledge about this API. Only report what you find in the source files.
Your task: Find warnings, errors, and implementation details for .
1. Search .claude/react/packages/*/src/ for:
- console.error mentions of
- console.warn mentions of
- Error messages mentioning
- The main implementation file
2. For each warning/error, document:
- The exact message text
- The condition that triggers it
- The source file and line number
Format your output as:
## Warnings & Errors
| Message | Trigger Condition | Source |
|---------|------------------|--------|
| "" | | |
## Implementation Notes
```
#### git-historian
```
You are researching React's .
CRITICAL: Do NOT rely on your prior knowledge. Only report what you find in git history.
Your task: Find commit messages that explain design decisions.
1. Run: cd .claude/react && git log --all --grep="" --oneline -50
2. For significant commits, read full message: git show --stat
3. Look for:
- Initial introduction of the API
- Bug fixes (reveal edge cases)
- Behavior changes
- Deprecation notices
Format your output as:
## Key Commits
### -
**Date:**
**Context:**
**Impact:**
```
#### pr-researcher
```
You are researching React's .
CRITICAL: Do NOT rely on your prior knowledge. Only report what you find in PRs.
Your task: Find PRs that introduced or modified .
1. Run: gh pr list -R facebook/react --search "" --state all --limit 20 --json number,title,url
2. For promising PRs, read details: gh pr view -R facebook/react
3. Look for:
- The original RFC/motivation
- Design discussions in comments
- Alternative approaches considered
- Breaking changes
Format your output as:
## Key PRs
### PR #:
**URL:**
**Summary:**
**Design Rationale:**
**Discussion Highlights:**
```
#### issue-hunter
```
You are researching React's .
CRITICAL: Do NOT rely on your prior knowledge. Only report what you find in issues.
Your task: Find issues that reveal common confusion about .
1. Search facebook/react: gh issue list -R facebook/react --search "" --state all --limit 20 --json number,title,url
2. Search reactjs/react.dev: gh issue list -R reactjs/react.dev --search "" --state all --limit 20 --json number,title,url
3. For each issue, identify:
- What the user was confused about
- What the resolution was
- Any gotchas revealed
Format your output as:
## Common Confusion
### Issue #:
**Repo:**
**Confusion:**
**Resolution:**
**Gotcha:**
```
#### types-inspector
```
You are researching React's .
CRITICAL: Do NOT rely on your prior knowledge. Only report what you find in type definitions.
Your task: Find and compare Flow and TypeScript type signatures for .
1. Flow types (source of truth): Search .claude/react/packages/*/src/*.js for @flow annotations related to
2. TypeScript types: Search .claude/react/packages/*/index.d.ts and @types/react
3. Compare and note any discrepancies
Format your output as:
## Flow Types (Source of Truth)
**File:**
```flow
```
## TypeScript Types
**File:**
```typescript
```
## Discrepancies
```
### Step 4: Synthesize Results
After all agents complete, combine their findings into a single research document.
**DO NOT add information from your own knowledge.** Only include what agents found in sources.
### Step 5: Save Output
Write the final document to `.claude/research/.md`
Replace spaces in topic with hyphens (e.g., "suspense boundaries" → "suspense-boundaries.md")
## Output Document Template
```markdown
# React Research:
> Generated by /react-expert on YYYY-MM-DD
> Sources: React repo (commit ), N PRs, M issues
## Summary
[Brief summary based SOLELY on source findings, not prior knowledge]
## API Signature
### Flow Types (Source of Truth)
[From types-inspector agent]
### TypeScript Types
[From types-inspector agent]
### Discrepancies
[Any differences between Flow and TS]
## Usage Examples
### From Tests
[From test-explorer agent - with file:line references]
### From PRs/Issues
[Real-world patterns from discussions]
## Caveats & Gotchas
[Each with source link]
- **** - Source:
## Warnings & Errors
| Message | Trigger Condition | Source File |
|---------|------------------|-------------|
[From source-explorer agent]
## Common Confusion
[From issue-hunter agent]
## Design Decisions
[From git-historian and pr-researcher agents]
## Source Links
### Commits
- :
### Pull Requests
- PR #: -
### Issues
- Issue #: -
```
## Common Mistakes to Avoid
1. **Trusting prior knowledge** - If you "know" something about the API, find the source evidence anyway
2. **Generating example code** - Every code example must come from an actual source file
3. **Skipping agents** - All 6 agents must run; each provides unique perspective
4. **Summarizing without sources** - Every claim needs a file:line or PR/issue reference
5. **Using web search** - No Stack Overflow, no blog posts, no social media
## Verification Checklist
Before finalizing the research document:
- [ ] React repo is at `.claude/react` with known commit hash
- [ ] All 6 agents were spawned in parallel
- [ ] Every code example has a source file reference
- [ ] Warnings/errors table has source locations
- [ ] No claims made without source evidence
- [ ] Discrepancies between Flow/TS types documented
- [ ] Source links section is complete
================================================
FILE: .claude/skills/review-docs/SKILL.md
================================================
---
name: review-docs
description: Use when reviewing React documentation for structure, components, and style compliance
---
CRITICAL: do not load these skills yourself.
Run these tasks in parallel for the given file(s). Each agent checks different aspects—not all apply to every file:
- [ ] Ask docs-reviewer agent to review {files} with docs-writer-learn (only for files in src/content/learn/).
- [ ] Ask docs-reviewer agent to review {files} with docs-writer-reference (only for files in src/content/reference/).
- [ ] Ask docs-reviewer agent to review {files} with docs-writer-blog (only for files in src/content/blog/).
- [ ] Ask docs-reviewer agent to review {files} with docs-voice (all documentation files).
- [ ] Ask docs-reviewer agent to review {files} with docs-components (all documentation files).
- [ ] Ask docs-reviewer agent to review {files} with docs-sandpack (files containing Sandpack examples).
If no file is specified, check git status for modified MDX files in `src/content/`.
The docs-reviewer will return a checklist of the issues it found. Respond with the full checklist and line numbers from all agents, and prompt the user to create a plan to fix these issues.
================================================
FILE: .claude/skills/write/SKILL.md
================================================
---
name: write
description: Use when creating new React documentation pages or updating existing ones. Accepts instructions like "add optimisticKey reference docs", "update ViewTransition with Activity", or "transition learn docs".
---
# Documentation Writer
Orchestrates research, writing, and review for React documentation.
## Invocation
```
/write add optimisticKey → creates new reference docs
/write update ViewTransition Activity → updates ViewTransition docs to cover Activity
/write transition learn docs → creates new learn docs for transitions
/write blog post for React 20 → creates a new blog post
```
## Workflow
```dot
digraph write_flow {
rankdir=TB;
"Parse intent" [shape=box];
"Research (parallel)" [shape=box];
"Synthesize plan" [shape=box];
"Write docs" [shape=box];
"Review docs" [shape=box];
"Issues found?" [shape=diamond];
"Done" [shape=doublecircle];
"Parse intent" -> "Research (parallel)";
"Research (parallel)" -> "Synthesize plan";
"Synthesize plan" -> "Write docs";
"Write docs" -> "Review docs";
"Review docs" -> "Issues found?";
"Issues found?" -> "Write docs" [label="yes - fix"];
"Issues found?" -> "Done" [label="no"];
}
```
### Step 1: Parse Intent
Determine from the user's instruction:
| Field | How to determine |
|-------|------------------|
| **Action** | "add"/"create"/"new" = new page; "update"/"edit"/"with" = modify existing |
| **Topic** | The React API or concept (e.g., `optimisticKey`, `ViewTransition`, `transitions`) |
| **Doc type** | "reference" (default for APIs/hooks/components), "learn" (for concepts/guides), "blog" (for announcements) |
| **Target file** | For updates: find existing file in `src/content/`. For new: determine path from doc type |
If the intent is ambiguous, ask the user to clarify before proceeding.
### Step 2: Research (Parallel Agents)
Spawn these agents **in parallel**:
#### Agent 1: React Expert Research
Use a Task agent (subagent_type: `general-purpose`) to invoke `/react-expert `. This researches the React source code, tests, PRs, issues, and type signatures.
**Prompt:**
```
Invoke the /react-expert skill for . Follow the skill's full workflow:
setup the React repo, dispatch all 6 research agents in parallel, synthesize
results, and save to .claude/research/.md. Return the full research document.
```
#### Agent 2: Existing Docs Audit
Use a Task agent (subagent_type: `Explore`) to find and read existing documentation for the topic.
**Prompt:**
```
Find all existing documentation related to in this repo:
1. Search src/content/ for files mentioning
2. Read any matching files fully
3. For updates: identify what sections exist and what's missing
4. For new pages: identify related pages to understand linking/cross-references
5. Check src/sidebarLearn.json and src/sidebarReference.json for navigation placement
Return: list of existing files with summaries, navigation structure, and gaps.
```
#### Agent 3: Use Case Research
Use a Task agent (subagent_type: `general-purpose`) with web search to find common use cases and patterns.
**Prompt:**
```
Search the web for common use cases and patterns for React's .
Focus on:
1. Real-world usage patterns developers actually need
2. Common mistakes or confusion points
3. Migration patterns (if replacing an older API)
4. Framework integration patterns (Next.js, Remix, etc.)
Return a summary of the top 5-8 use cases with brief code sketches.
Do NOT search Stack Overflow. Focus on official docs, GitHub discussions,
and high-quality technical blogs.
```
### Step 3: Synthesize Writing Plan
After all research agents complete, create a writing plan that includes:
1. **Page type** (from docs-writer-reference decision tree or learn/blog type)
2. **File path** for the new or updated file
3. **Outline** with section headings matching the appropriate template
4. **Content notes** for each section, drawn from research:
- API signature and parameters (from react-expert types)
- Usage examples (from react-expert tests + use case research)
- Caveats and pitfalls (from react-expert warnings/errors/issues)
- Cross-references to related pages (from docs audit)
5. **Navigation changes** needed (sidebar JSON updates)
Present this plan to the user and confirm before proceeding.
### Step 4: Write Documentation
Dispatch a Task agent (subagent_type: `general-purpose`) to write the documentation.
**The agent prompt MUST include:**
1. The full writing plan from Step 3
2. An instruction to invoke the appropriate skill:
- `/docs-writer-reference` for reference pages
- `/docs-writer-learn` for learn pages
- `/docs-writer-blog` for blog posts
3. An instruction to invoke `/docs-components` for MDX component patterns
4. An instruction to invoke `/docs-sandpack` if adding interactive code examples
5. The research document content (key findings, not the full dump)
**Prompt template:**
```
You are writing React documentation. Follow these steps:
1. Invoke /docs-writer- to load the page template and conventions
2. Invoke /docs-components to load MDX component patterns
3. Invoke /docs-sandpack if you need interactive code examples
4. Write the documentation following the plan below
PLAN:
RESEARCH FINDINGS:
Write the file to:
Also update if adding a new page.
```
### Step 5: Review Documentation
Invoke `/review-docs` on the written files. This dispatches parallel review agents checking:
- Structure compliance (docs-writer-*)
- Voice and style (docs-voice)
- Component usage (docs-components)
- Sandpack patterns (docs-sandpack)
### Step 6: Fix Issues
If the review finds issues:
1. Present the review checklist to the user
2. Fix the issues identified
3. Re-run `/review-docs` on the fixed files
4. Repeat until clean
## Important Rules
- **Always research before writing.** Never write docs from LLM knowledge alone.
- **Always confirm the plan** with the user before writing.
- **Always review** with `/review-docs` after writing.
- **Match existing patterns.** Read neighboring docs to match style and depth.
- **Update navigation.** New pages need sidebar entries.
================================================
FILE: .editorconfig
================================================
root = true
[*]
charset = utf-8
end_of_line = lf
insert_final_newline = true
indent_style = space
================================================
FILE: .eslintignore
================================================
scripts
plugins
next.config.js
.claude/
worker-bundle.dist.js
================================================
FILE: .eslintrc
================================================
{
"root": true,
"extends": "next/core-web-vitals",
"parser": "@typescript-eslint/parser",
"plugins": ["@typescript-eslint", "eslint-plugin-react-compiler", "local-rules"],
"rules": {
"no-unused-vars": "off",
"@typescript-eslint/no-unused-vars": ["error", {"varsIgnorePattern": "^_"}],
"react-hooks/exhaustive-deps": "error",
"react/no-unknown-property": ["error", {"ignore": ["meta"]}],
"react-compiler/react-compiler": "error",
"local-rules/lint-markdown-code-blocks": "error"
},
"env": {
"node": true,
"commonjs": true,
"browser": true,
"es6": true
},
"overrides": [
{
"files": ["src/content/**/*.md"],
"parser": "./eslint-local-rules/parser",
"parserOptions": {
"sourceType": "module"
},
"rules": {
"no-unused-vars": "off",
"@typescript-eslint/no-unused-vars": "off",
"react-hooks/exhaustive-deps": "off",
"react/no-unknown-property": "off",
"react-compiler/react-compiler": "off",
"local-rules/lint-markdown-code-blocks": "error"
}
}
]
}
================================================
FILE: .gitattributes
================================================
# https://git-scm.com/docs/gitattributes
# Ensure consistent EOL(LF) for all files that Git considers text files.
* text=auto eol=lf
================================================
FILE: .github/CODEOWNERS
================================================
* @hg-pyun @gnujoow @eomttt @lumirlumir
================================================
FILE: .github/FUNDING.yml
================================================
github: [lumirlumir]
================================================
FILE: .github/ISSUE_TEMPLATE/0-bug.yml
================================================
name: "🐛 Report a bug"
description: "Report a problem on the website."
title: "[Bug]: "
labels: ["bug: unconfirmed"]
body:
- type: textarea
attributes:
label: Summary
description: |
A clear and concise summary of what the bug is.
placeholder: |
Example bug report:
When I click the "Submit" button on "Feedback", nothing happens.
validations:
required: true
- type: input
attributes:
label: Page
description: |
What page(s) did you encounter this bug on?
placeholder: |
https://react.dev/
validations:
required: true
- type: textarea
attributes:
label: Details
description: |
Please provide any additional details about the bug.
placeholder: |
Example details:
The "Submit" button is unresponsive. I've tried refreshing the page and using a different browser, but the issue persists.
validations:
required: false
================================================
FILE: .github/ISSUE_TEMPLATE/1-typo.yml
================================================
name: "🤦 Typo or mistake"
description: "Report a typo or mistake in the docs."
title: "[Typo]: "
labels: ["type: typos"]
body:
- type: textarea
attributes:
label: Summary
description: |
A clear and concise summary of what the mistake is.
placeholder: |
Example:
The code example on the "useReducer" page includes an unused variable `nextId`.
validations:
required: true
- type: input
attributes:
label: Page
description: |
What page is the typo on?
placeholder: |
https://react.dev/
validations:
required: true
- type: textarea
attributes:
label: Details
description: |
Please provide a explanation for why this is a mistake.
placeholder: |
Example mistake:
In the "useReducer" section of the "API Reference" page, the code example under "Writing a reducer function" includes an unused variable `nextId` that should be removed.
validations:
required: false
================================================
FILE: .github/ISSUE_TEMPLATE/3-framework.yml
================================================
name: "📄 Suggest new framework"
description: "I am a framework author applying to be included as a recommended framework."
title: "[Framework]: "
labels: ["type: framework"]
body:
- type: markdown
attributes:
value: |
## Apply to be included as a recommended React framework
_This form is for framework authors to apply to be included as a recommended [React framework](https://react.dev/learn/creating-a-react-app). If you are not a framework author, please contact the authors before submitting._
Our goal when recommending a framework is to start developers with a React project that solves common problems like code splitting, data fetching, routing, and HTML generation without any extra work later. We believe this will allow users to get started quickly with React, and scale their app to production.
While we understand that many frameworks may want to be featured, this page is not a place to advertise every possible React framework or all frameworks that you can add React to. There are many great frameworks that offer support for React that are not listed in our guides. The frameworks we recommend have invested significantly in the React ecosystem, and collaborated with the React team to be compatible with our [full-stack React architecture vision](https://react.dev/learn/creating-a-react-app#which-features-make-up-the-react-teams-full-stack-architecture-vision).
To be included, frameworks must meet the following criteria:
- **Free & open-source**: must be open source and free to use.
- **Well maintained**. must be actively maintained, providing bug fixes and improvements.
- **Active community**: must have a sufficiently large and active community to support users.
- **Clear onboarding**: must have clear install steps to install the React version of the framework.
- **Ecosystem compatibility**: must support using the full range of libraries and tools in the React ecosystem.
- **Self-hosting option**: must support an option to self-host applications without losing access to features.
- **Developer experience**. must allow developers to be productive by supporting features like Fast Refresh.
- **User experience**. must provide built-in support for common problems like routing and data-fetching.
- **Compatible with our future vision for React**. React evolves over time, and frameworks that do not align with React’s direction risk isolating their users from the main React ecosystem over time. To be included on this page we must feel confident that the framework is setting its users up for success with React over time.
Please note, we have reviewed most of the popular frameworks available today, so it is unlikely we have not considered your framework already. But if you think we missed something, please complete the application below.
- type: input
attributes:
label: Name
description: |
What is the name of your framework?
validations:
required: true
- type: input
attributes:
label: Homepage
description: |
What is the URL of your homepage?
validations:
required: true
- type: input
attributes:
label: Install instructions
description: |
What is the URL of your getting started guide?
validations:
required: true
- type: dropdown
attributes:
label: Is your framework open source?
description: |
We only recommend free and open source frameworks.
options:
- 'No'
- 'Yes'
validations:
required: true
- type: textarea
attributes:
label: Well maintained
description: |
Please describe how your framework is actively maintained. Include recent releases, bug fixes, and improvements as examples.
validations:
required: true
- type: textarea
attributes:
label: Active community
description: |
Please describe your community. Include the size of your community, and links to community resources.
validations:
required: true
- type: textarea
attributes:
label: Clear onboarding
description: |
Please describe how a user can install your framework with React. Include links to any relevant documentation.
validations:
required: true
- type: textarea
attributes:
label: Ecosystem compatibility
description: |
Please describe any limitations your framework has with the React ecosystem. Include any libraries or tools that are not compatible with your framework.
validations:
required: true
- type: textarea
attributes:
label: Self-hosting option
description: |
Please describe how your framework supports self-hosting. Include any limitations to features when self-hosting. Also include whether you require a server to deploy your framework.
validations:
required: true
- type: textarea
attributes:
label: Developer Experience
description: |
Please describe how your framework provides a great developer experience. Include any limitations to React features like React DevTools, Chrome DevTools, and Fast Refresh.
validations:
required: true
- type: textarea
attributes:
label: User Experience
description: |
Please describe how your framework helps developers create high quality user experiences by solving common use-cases. Include specifics for how your framework offers built-in support for code-splitting, routing, HTML generation, and data-fetching in a way that avoids client/server waterfalls by default. Include details on how you offer features such as SSG and SSR.
validations:
required: true
- type: textarea
attributes:
label: Compatible with our future vision for React
description: |
Please describe how your framework aligns with our future vision for React. Include how your framework will evolve with React over time, and your plans to support future React features like React Server Components.
validations:
required: true
================================================
FILE: .github/ISSUE_TEMPLATE/config.yml
================================================
contact_links:
- name: 📃 Bugs in React
url: https://github.com/facebook/react/issues/new/choose
about: This issue tracker is not for bugs in React. Please file React issues here.
- name: 🤔 Questions and Help
url: https://reactjs.org/community/support.html
about: This issue tracker is not for support questions. Please refer to the React community's help and discussion forums.
================================================
FILE: .github/ISSUE_TEMPLATE/need-translation.md
================================================
---
name: 번역 필요
about: 번역이 필요한 문서에 대해 이 템플릿을 사용해주세요
title: '[번역 필요]: '
labels: 'need translation'
assignees: ''
---
## Summary
## Page
## Details
================================================
FILE: .github/ISSUE_TEMPLATE/term.md
================================================
---
name: Term
about: 문서에서 사용되는 용어 번역에 논의가 필요한 경우 이 템플릿을 사용해주세요
title: ''
labels: discussion, term
assignees: ''
---
## 논의하고자 하는 용어
## 해당 용어가 등장하는 원문의 문장
---
## 논의가 완료된 후
1. 아래 코드를 업데이트 해주세요.
- [ ] [`/textlint/data/rules/translateGlossary.js`](https://github.com/reactjs/ko.react.dev/blob/main/textlint/data/rules/translateGlossary.js)
2. 용어 사전에 업데이트된 내역이 정상 반영되었나 확인해주세요. (해당 내역은 husky의 pre-commit 훅을 통해 자동 업데이트 됩니다.)
- [ ] [`/wiki/translate-glossary.md`](https://github.com/reactjs/ko.react.dev/blob/main/wiki/translate-glossary.md)
================================================
FILE: .github/PULL_REQUEST_TEMPLATE.md
================================================
#
## 필수 확인 사항
- [ ] [기여자 행동 강령 규약Code of Conduct](https://github.com/reactjs/ko.react.dev/blob/main/CODE_OF_CONDUCT.md)
- [ ] [기여 가이드라인Contributing](https://github.com/reactjs/ko.react.dev/blob/main/CONTRIBUTING.md)
- [ ] [공통 스타일 가이드Universal Style Guide](https://github.com/reactjs/ko.react.dev/blob/main/wiki/universal-style-guide.md)
- [ ] [번역을 위한 모범 사례Best Practices for Translation](https://github.com/reactjs/ko.react.dev/blob/main/wiki/best-practices-for-translation.md)
- [ ] [번역 용어 정리Translate Glossary](https://github.com/reactjs/ko.react.dev/blob/main/wiki/translate-glossary.md)
- [ ] [`textlint` 가이드Textlint Guide](https://github.com/reactjs/ko.react.dev/blob/main/wiki/textlint-guide.md)
- [ ] [맞춤법 검사Spelling Check](https://nara-speller.co.kr/speller/)
## 선택 확인 사항
- [ ] 번역 초안 작성Draft Translation
- [ ] 리뷰 반영Resolve Reviews
================================================
FILE: .github/dependabot.yml
================================================
version: 2
updates:
- package-ecosystem: "npm"
directory: "/"
schedule:
interval: "weekly"
# Disable Dependabot. Doing it here so it propagates to translation forks.
open-pull-requests-limit: 0
================================================
FILE: .github/workflows/analyze.yml
================================================
name: Analyze Bundle
on:
pull_request:
push:
branches:
- main # change this if your default branch is named differently
workflow_dispatch:
permissions: {}
jobs:
analyze:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Set up node
uses: actions/setup-node@v4
with:
node-version: '20.x'
cache: yarn
cache-dependency-path: yarn.lock
- name: Restore cached node_modules
uses: actions/cache@v4
with:
path: '**/node_modules'
key: node_modules-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock') }}
- name: Install deps
run: yarn install --frozen-lockfile
- name: Restore next build
uses: actions/cache@v4
id: restore-build-cache
env:
cache-name: cache-next-build
with:
path: .next/cache
# change this if you prefer a more strict cache
key: ${{ runner.os }}-build-${{ env.cache-name }}
- name: Build next.js app
# change this if your site requires a custom build command
run: ./node_modules/.bin/next build
# Here's the first place where next-bundle-analysis' own script is used
# This step pulls the raw bundle stats for the current bundle
- name: Analyze bundle
run: npx -p nextjs-bundle-analysis@0.5.0 report
- name: Upload bundle
uses: actions/upload-artifact@v4
with:
path: .next/analyze/__bundle_analysis.json
name: bundle_analysis.json
- name: Download base branch bundle stats
uses: dawidd6/action-download-artifact@268677152d06ba59fcec7a7f0b5d961b6ccd7e1e
if: success() && github.event.number
with:
workflow: analyze.yml
branch: ${{ github.event.pull_request.base.ref }}
name: bundle_analysis.json
path: .next/analyze/base/bundle
# And here's the second place - this runs after we have both the current and
# base branch bundle stats, and will compare them to determine what changed.
# There are two configurable arguments that come from package.json:
#
# - budget: optional, set a budget (bytes) against which size changes are measured
# it's set to 350kb here by default, as informed by the following piece:
# https://infrequently.org/2021/03/the-performance-inequality-gap/
#
# - red-status-percentage: sets the percent size increase where you get a red
# status indicator, defaults to 20%
#
# Either of these arguments can be changed or removed by editing the `nextBundleAnalysis`
# entry in your package.json file.
- name: Compare with base branch bundle
if: success() && github.event.number
run: ls -laR .next/analyze/base && npx -p nextjs-bundle-analysis compare
- name: Upload analysis comment
uses: actions/upload-artifact@v4
with:
name: analysis_comment.txt
path: .next/analyze/__bundle_analysis_comment.txt
- name: Save PR number
run: echo ${{ github.event.number }} > ./pr_number
- name: Upload PR number
uses: actions/upload-artifact@v4
with:
name: pr_number
path: ./pr_number
# The actual commenting happens in the other action, matching the guidance in
# https://securitylab.github.com/research/github-actions-preventing-pwn-requests/
================================================
FILE: .github/workflows/analyze_comment.yml
================================================
name: Analyze Bundle (Comment)
on:
workflow_run:
workflows: ['Analyze Bundle']
types:
- completed
permissions:
contents: read
issues: write
pull-requests: write
jobs:
comment:
runs-on: ubuntu-latest
if: >
${{ github.event.workflow_run.event == 'pull_request' &&
github.event.workflow_run.conclusion == 'success' }}
steps:
- name: Download base branch bundle stats
uses: dawidd6/action-download-artifact@268677152d06ba59fcec7a7f0b5d961b6ccd7e1e
with:
workflow: analyze.yml
run_id: ${{ github.event.workflow_run.id }}
name: analysis_comment.txt
path: analysis_comment.txt
- name: Download PR number
uses: dawidd6/action-download-artifact@268677152d06ba59fcec7a7f0b5d961b6ccd7e1e
with:
workflow: analyze.yml
run_id: ${{ github.event.workflow_run.id }}
name: pr_number
path: pr_number
- name: Get comment body
id: get-comment-body
if: success()
run: |
echo 'body<> $GITHUB_OUTPUT
echo '' >> $GITHUB_OUTPUT
echo '## Size changes' >> $GITHUB_OUTPUT
echo '' >> $GITHUB_OUTPUT
echo '' >> $GITHUB_OUTPUT
echo '' >> $GITHUB_OUTPUT
cat analysis_comment.txt/__bundle_analysis_comment.txt >> $GITHUB_OUTPUT
echo '' >> $GITHUB_OUTPUT
echo '' >> $GITHUB_OUTPUT
echo '' >> $GITHUB_OUTPUT
echo 'EOF' >> $GITHUB_OUTPUT
pr_number=$(cat pr_number/pr_number)
echo "pr-number=$pr_number" >> $GITHUB_OUTPUT
- name: Comment
uses: marocchino/sticky-pull-request-comment@52423e01640425a022ef5fd42c6fb5f633a02728
with:
header: next-bundle-analysis
number: ${{ steps.get-comment-body.outputs.pr-number }}
message: ${{ steps.get-comment-body.outputs.body }}
================================================
FILE: .github/workflows/site_lint.yml
================================================
name: Site Lint / Heading ID check
on:
push:
branches:
- main # change this if your default branch is named differently
pull_request:
types: [opened, synchronize, reopened]
permissions: {}
jobs:
lint:
runs-on: ubuntu-latest
name: Lint on node 20.x and ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Use Node.js 20.x
uses: actions/setup-node@v4
with:
node-version: 20.x
cache: yarn
cache-dependency-path: yarn.lock
- name: Restore cached node_modules
uses: actions/cache@v4
with:
path: '**/node_modules'
key: node_modules-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock') }}
- name: Install deps
run: yarn install --frozen-lockfile
- name: Lint codebase
run: yarn ci-check
================================================
FILE: .github/workflows/textlint_lint.yml
================================================
name: Textlint Lint
on:
push:
branches:
- main
paths:
- 'src/**/*.md'
- 'textlint/**/*.js'
- '.github/workflows/textlint_lint.yml'
- 'package.json'
pull_request:
types:
- opened
- synchronize
- reopened
paths:
- 'src/**/*.md'
- 'textlint/**/*.js'
- '.github/workflows/textlint_lint.yml'
- 'package.json'
jobs:
Lint:
runs-on: ubuntu-latest
steps:
- name: Set up checkout
uses: actions/checkout@v4
- name: Set up node
uses: actions/setup-node@v4
with:
node-version: 20.x
cache: yarn
- name: Set up cache
uses: actions/cache@v4
with:
path: ~/.yarn-cache
key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}
- name: Install dependencies
run: yarn install --frozen-lockfile
# The `--frozen-lockfile` flag in Yarn ensures that dependencies are installed without modifying the `yarn.lock` file. It is useful for maintaining consistency in CI/CD environments by preventing unexpected changes to the lock file and ensuring that the same versions of dependencies are installed.
- name: Lint
run: yarn textlint-lint
================================================
FILE: .github/workflows/textlint_test.yml
================================================
name: Textlint Test
on:
push:
branches:
- main
paths:
- 'textlint/**/*.js'
- '.github/workflows/textlint_test.yml'
pull_request:
types:
- opened
- synchronize
- reopened
paths:
- 'textlint/**/*.js'
- '.github/workflows/textlint_test.yml'
jobs:
Test:
runs-on: ubuntu-latest
steps:
- name: Set up checkout
uses: actions/checkout@v4
- name: Set up node
uses: actions/setup-node@v4
with:
node-version: 20.x
cache: yarn
- name: Set up cache
uses: actions/cache@v4
with:
path: ~/.yarn-cache
key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}
- name: Install dependencies
run: yarn install --frozen-lockfile
# The `--frozen-lockfile` flag in Yarn ensures that dependencies are installed without modifying the `yarn.lock` file. It is useful for maintaining consistency in CI/CD environments by preventing unexpected changes to the lock file and ensuring that the same versions of dependencies are installed.
- name: Test
run: yarn textlint-test
================================================
FILE: .gitignore
================================================
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
# dependencies
node_modules
/.pnp
.pnp.js
# testing
/coverage
# next.js
/.next/
/out/
# production
/build
# misc
.DS_Store
*.pem
tsconfig.tsbuildinfo
# debug
npm-debug.log*
yarn-debug.log*
yarn-error.log*
# local env files
.env.local
.env.development.local
.env.test.local
.env.production.local
# vercel
.vercel
# external fonts
public/fonts/**/Optimistic_*.woff2
# rss
public/rss.xml
# code editor
.cursor
.idea
*.code-workspace
# claude local settings
.claude/*.local.*
.claude/react/
# worktrees
.worktrees/
================================================
FILE: .husky/common.sh
================================================
#!/bin/sh
command_exists () {
command -v "$1" >/dev/null 2>&1
}
# Windows 10, Git Bash and Yarn workaround
if command_exists winpty && test -t 1; then
exec < /dev/tty
fi
================================================
FILE: .husky/pre-commit
================================================
#!/bin/sh
. "$(dirname "$0")/_/husky.sh"
. "$(dirname "$0")/common.sh"
yarn lint-staged
================================================
FILE: .prettierignore
================================================
src/content/**/*.md
src/components/MDX/Sandpack/sandpack-rsc/sandbox-code/src/worker-bundle.dist.js
================================================
FILE: .prettierrc
================================================
{
"bracketSpacing": false,
"singleQuote": true,
"bracketSameLine": true,
"trailingComma": "es5",
"printWidth": 80,
"overrides": [
{
"files": "*.css",
"options": {
"parser": "css"
}
},
{
"files": "*.md",
"options": {
"parser": "mdx"
}
}
]
}
================================================
FILE: .textlintrc.js
================================================
module.exports = {
rules: {
'allowed-uris': {
disallowed: {
/**
* Disallow URIs starting with the following strings:
* - https://ko.react.dev
* - http://ko.react.dev
*
* For example,
* `https://ko.react.dev/reference/rules` can be replaced with `/reference/rules`.
*/
links: [/https?:\/\/ko\.react\.dev/g],
},
},
},
filters: {
comments: true,
},
};
================================================
FILE: .vscode/extensions.json
================================================
{
"recommendations": [
"dbaeumer.vscode-eslint",
"esbenp.prettier-vscode",
"editorconfig.editorconfig"
]
}
================================================
FILE: .vscode/settings.json
================================================
{
"editor.formatOnSave": true,
"editor.formatOnPaste": true,
"editor.defaultFormatter": "esbenp.prettier-vscode",
"editor.codeActionsOnSave": {
"source.fixAll.eslint": "always"
}
}
================================================
FILE: CLAUDE.md
================================================
# CLAUDE.md
This file provides guidance to Claude Code when working with this repository.
## Project Overview
This is the React documentation website (react.dev), built with Next.js 15.1.11 and React 19. Documentation is written in MDX format.
## Development Commands
```bash
yarn build # Production build
yarn lint # Run ESLint
yarn lint:fix # Auto-fix lint issues
yarn tsc # TypeScript type checking
yarn check-all # Run prettier, lint:fix, tsc, and rss together
```
## Project Structure
```
src/
├── content/ # Documentation content (MDX files)
│ ├── learn/ # Tutorial/learning content
│ ├── reference/ # API reference docs
│ ├── blog/ # Blog posts
│ └── community/ # Community pages
├── components/ # React components
├── pages/ # Next.js pages
├── hooks/ # Custom React hooks
├── utils/ # Utility functions
└── styles/ # CSS/Tailwind styles
```
## Code Conventions
### TypeScript/React
- Functional components only
- Tailwind CSS for styling
### Documentation Style
When editing files in `src/content/`, the appropriate skill will be auto-suggested:
- `src/content/learn/` - Learn page structure and tone
- `src/content/reference/` - Reference page structure and tone
For MDX components (DeepDive, Pitfall, Note, etc.), invoke `/docs-components`.
For Sandpack code examples, invoke `/docs-sandpack`.
See `.claude/docs/react-docs-patterns.md` for comprehensive style guidelines.
Prettier is used for formatting (config in `.prettierrc`).
================================================
FILE: CODE_OF_CONDUCT.md
================================================
# 기여자 행동 강령 규약
## 서약
우리는 구성원, 기여자 및 리더로서 커뮤니티에 참여하여
연령, 신체 크기, 눈에 보이거나 보이지 않는 장애, 민족성, 성별, 성 정체성과 표현,
경력, 학력, 사회 경제적 지위, 국적, 외모, 인종, 카스트 제도, 피부색, 종교
또는 성적 정체성과 성적 성향에 관계없이 모든 사람을 차별하지 않을 것을 서약한다.
우리는 개방적이고 친근하며 다양하고 포용적이며 건강한 커뮤니티에 기여하는
방식으로 행동하고 상호작용할 것을 서약한다.
## 표준
커뮤니티의 긍정적인 환경을 위해 기여자가 해야 할 행동은 다음과 같다.
- 다른 사람들에 대한 친절과 공감 표현
- 서로 다른 의견 및 관점, 경험에 대한 존중
- 건설적인 피드백을 제공 및 열린 마음으로 수락
- 책임을 받아들이고 실수로 인해 영향을 받은 사람들에게 사과하며 경험을 통해 배움
- 개인뿐만 아닌 전체 커뮤니티를 위한 최선의 방법에 집중
하지말아야 할 행동은 다음과 같다.
- 성적인 언어와 이미지 사용, 성적 관심이나 어떤 종류의 접근
- 소모적인 논쟁, 모욕적 또는 비하하는 댓글과 개인적 또는 정치적인 공격
- 공개적이거나 개인적인 괴롭힘
- 동의없는 집주소 또는 이메일 주소 등의 개인 정보의 공개
- 부적절한 것으로 간주될 수 있는 다른 행위
## 집행 책임
커뮤니티 리더는 허용되는 행동의 기준을 명확히 하고 집행할 책임이 있으며
부적절하다고 여겨지는 모든 행동, 위협, 공격 또는 피해에 대해 적절하고
공정한 행동을 취한다.
프로젝트 유지자는 이 행동 강령을 따르지 않은 댓글, 커밋, 코드, 위키 편집,
이슈와 그 외 다른 기여를 삭제, 수정 또는 거부할 권리와 책임이 있다. 또한,
부적당하거나 험악하거나 공격적이거나 해롭다고 생각하는 다른 행동을 한 기여자를
일시적 또는 영구적으로 퇴장시킬 수 있다.
커뮤니티 리더는 이 행동 강령을 따르지 않는 댓글, 커밋, 코드, 위키 편집,
이슈와 그 외 다른 기여를 삭제, 수정 또는 거부할 권리와 책임이 있으며,
적절한 경우 중재적 의사결정에 대한 이유를 전달할 것이다.
## 범위
이 행동 강령은 개인이 공개 영역에서 커뮤니티를 공식적으로 대표할 때를
포함하여 모든 커뮤니티 영역에 적용된다.
커뮤니티 대표의 예로 공식 이메일 주소 사용, 공식 소셜 미디어 계정을 통한 게시,
온/오프라인 이벤트에서 임명된 대표자의 활동이 있다.
## 집행
모욕적이거나 괴롭힘 또는 그 외 하지말아야 할 행동을 발견하면
을 통해 집행 책임이 있는 커뮤니티 리더에게 보고한다.
모든 불만사항은 신속하고 공정하게 검토되고 조사될 것이다.
커뮤니티 리더는 사건의 보고자의 사생활과 안전을 존중할 의무가 있다.
## 집행 지침
커뮤니티 리더는 행동 강령 위반으로 간주되는 행동에 대한 결과를 결정할 때,
다음의 커뮤니티 영향 지침을 준수한다:
### 1. 정정
**커뮤니티 영향**: 커뮤니티 내 부적절한 언어 사용이나
비전문적인 행동 또는 불쾌함을 주는 행동.
**결과**: 커뮤니티 리더가 별도로 위반에 대한 명확성과 부적절함에 대한
이유를 설명하고 서면 경고.
공개 사과를 요청할 수 있다.
### 2. 경고
**커뮤니티 영향**: 단일 사고 또는 연속된 행동 위반.
**결과**: 지속적인 행동에 대한 결과에 대해 경고.
특정 기간동안 행동 강령을 시행하는 사람들과의 원치 않는 상호작용을 포함한
관련된 사람들과의 상호작용 금지. 소셜 미디어와 같은 외부 채널뿐만 아닌
커뮤니티 공간에서의 상호작용도 금지된다.
이 조항을 위반하면 일시적 혹은 영구적으로 제재로 이어질 수 있다.
### 3. 일시적인 제재
**커뮤니티 영향**: 지속적으로 부적절한 행동을 포함한
심각한 커뮤니티 기준 위반.
**결과**: 특정 기간동안 커뮤니티와의 어떠한 종류의 상호작용이나
공개적 소통이 일시적 제재.
이 기간동안 행동 강령을 시행하는 사람들과의 원치 않는 상호작용을 포함한
관련된 사람들과의 상호작용 금지. 소셜 미디어와 같은 외부 채널뿐만 아닌
커뮤니티 공간에서의 상호작용도 금지된다.
이 조항을 위반하면 일시적 혹은 영구적으로 제재로 이어질 수 있다.
### 4. 영구 제재
**커뮤니티 영향**: 지속적인 부적절한 행동, 개인적인 괴롭힘 또는
개인의 계급에 대한 공격이나 폄하를 포함한 커뮤니티 표준 위반 패턴을 보임.
**결과**: 커뮤니티와의 모든 종류의 공개적 교류를 영구적으로 제재.
## 참고
이 행동 강령은 [기여자 규약][homepage] 의 2.1 버전을 변형하였습니다. 그 내용은
[https://www.contributor-covenant.org/ko/version/2/1/code-of-conduct.html][v2.1]
에서 확인할 수 있습니다.
커뮤니티 영향 지침은 [Mozilla's code of conduct enforcement ladder][Mozilla CoC]
에서 영감을 얻었습니다.
이 행동 강령에 관련한 일반적인 질문에 대한 대답은
[https://www.contributor-covenant.org/faq][FAQ]를 참고할 수 있습니다.
번역본은 [https://www.contributor-covenant.org/translations][translations]에서
볼 수 있습니다.
[homepage]: https://www.contributor-covenant.org
[v2.1]: https://www.contributor-covenant.org/version/2/1/code_of_conduct.html
[Mozilla CoC]: https://github.com/mozilla/diversity
[FAQ]: https://www.contributor-covenant.org/faq
[translations]: https://www.contributor-covenant.org/translations
================================================
FILE: CONTRIBUTING.md
================================================
# 기여하기
React 문서 기여에 관심을 가져주셔서 감사합니다!
## 행동 강령
페이스북Facebook은 프로젝트 참가자가 준수해야 하는 행동 강령을 채택했습니다. [전문을 읽어보세요](https://code.facebook.com/codeofconduct). 어떤 행동이 허용되고 허용되지 않는지 확인할 수 있습니다.
## 기술 문서 작성 팁
기술 문서를 작성할 때 염두에 두어야 할 사항에 대한 [좋은 요약](https://medium.com/@kvosswinkel/coding-like-a-journalist-ee52360a16bc)입니다.
## 글에 대한 가이드라인
**섹션마다 의도적으로 다른 스타일을 사용합니다.**
이 문서는 다양한 학습 스타일과 사용 사례를 고려하여 분할되어 있습니다. 본문을 수정할 때는 주변 글의 톤Tone과 스타일Style에 맞게 작성하도록 주의하세요. 새로운 글을 작성할 때는 같은 섹션에 있는 다른 글들과 톤을 맞추도록 하세요. 각 섹션의 의도와 동기는 아래에서 확인할 수 있습니다.
**[React 학습하기](https://ko.react.dev/learn)** 섹션은 기초 개념을 단계별로 소개하기 위해 만들어졌습니다. 여기서 제공되는 글들은 이전에 설명된 지식을 바탕으로 하므로, 글 간 앞뒤 개념이 중복되거나 꼬이지 않도록 주의하세요. 독자는 첫 번째 글부터 마지막 글까지 순서대로 읽으며 개념을 익힐 수 있어야 하며, 추가 설명을 위해 미리 앞선 개념들을 살펴보지 않도록 해야 합니다. 이런 이유로 상태State는 이벤트Event보다 먼저 설명되고, 'React로 사고하기' 파트에서 `ref`를 사용하지 않는 등 특정 순서가 정해져 있습니다. 동시에 'React 학습하기'는 React 개념에 대한 참고 자료 역할을 하므로, 개념들에 대한 정의와 상호 관계를 엄격하게 다루어야 합니다.
**[API 참고서](https://ko.react.dev/reference/react)** 섹션은 개념이 아닌 API별로 정리되어 있으며, 가능한 한 모든 경우를 포함하는 것을 목표로 합니다. 'React 학습하기'에서 간단히 다뤘거나 생략한 예외 사항Edge Cases 혹은 권장 사항Recommendations은 해당 API의 레퍼런스 문서에 추가로 언급해야 합니다.
**스스로 작성한 지침Instructions을 실천해 보세요.**
예를 들어, 단계별 가이드를 작성한다면, 직접 그 지침을 따라가 보며 누락된 내용이나 순서가 맞지 않는 부분을 찾아보세요. 실제로 지침을 순서대로 진행하다보면, 작성자가 설명하지 않은 배경지식이 있거나, 단계가 뒤섞여 있는 등의 문제를 발견할 수 있습니다. 가능하다면 다른 사람에게 지침을 따라보게 하고, 그들이 어려움을 겪는 부분을 관찰하는 것도 좋은 방법입니다. 사소해 보이지만 예상치 못한 곳에서 문제가 생길 수 있습니다.
## 코드 예시에 대한 가이드라인
### 구문Syntax
#### 가능하면 `createElement` 대신 JSX를 사용하세요
단, `createElement` 자체를 설명해야 하는 경우는 예외입니다.
#### 가능하면 `const`, 필요한 경우에는 `let`을 사용하고, `var`는 사용하지 마세요
ES5만 다루는 경우라면 이 규칙은 무시하세요.
#### ES5의 기능만으로 간단하게 작성할 수 있는 경우, ES6의 기능을 무조건적으로 사용하지 마세요
ES6가 아직 낯선 사람도 많습니다. 이미 여러 곳에서 `const` / `let`, 클래스, 화살표 함수 등을 사용하고 있지만, 그에 상응하는 ES5 코드가 간단하고 가독성이 좋다면 ES5를 사용하는 것도 고려하세요.
특히 최상위 함수에서는 `const myFunction = () => ...`과 같은 화살표 함수 대신에 이름 있는 `function` 선언을 선호합니다. 하지만 컴포넌트 내 `this` 컨텍스트를 유지해야 하는 경우에는 화살표 함수를 사용하세요. 새로운 문법을 사용할 때는 장단점을 모두 따져보고 결정하세요.
#### 아직 표준화되지 않은 기능은 사용하지 마세요
예를 들어, 다음 코드처럼 작성하지 마세요.
```js
class MyComponent extends React.Component {
state = {value: ''};
handleChange = (e) => {
this.setState({value: e.target.value});
};
}
```
대신, 다음처럼 작성하세요.
```js
class MyComponent extends React.Component {
constructor(props) {
super(props);
this.handleChange = this.handleChange.bind(this);
this.state = {value: ''};
}
handleChange(e) {
this.setState({value: e.target.value});
}
}
```
실험적인 제안Experimental Proposal에 대해 설명하는 경우라면 예외로 하되, 코드와 주변 글에서 실험적임Experimental을 명시하세요.
### 스타일
- 세미콜론을 사용하세요.
- 함수 이름과 괄호 사이에는 공백을 넣지 마세요. (`method () {}`가 아닌, `method() {}` 형태.)
- 고민될 때는 [Prettier](https://prettier.io/playground/)의 기본 스타일을 따르세요.
- Hooks, Effects, Transitions 같은 React 관련 개념은 항상 대문자로 시작하세요.
### 하이라이팅
마크다운Markdown의 코드 블록에서는 `js`를 사용하세요:
````
```js
// 코드
```
````
간혹 숫자와 함께 사용되는 블록이 있습니다.
이는 특정 줄을 강조하기 위한 용도입니다.
한 줄을 강조하는 예시.
````
```js {2}
function hello() {
// 이 줄이 강조됩니다
}
```
````
일정 범위를 강조하는 예시.
````
```js {2-4}
function hello() {
// 여기부터
// 시작해서
// 여기까지 강조됩니다
}
```
````
여러 범위를 강조하는 예시.
````
```js {2-4,6}
function hello() {
// 여기부터
// 시작해서
// 여기까지 강조됩니다
console.log('hello');
// 이 줄도 강조됩니다
console.log('there');
}
```
````
코드를 이동하거나 순서를 바꿨다면, 강조하는 줄도 같이 수정해야 한다는 점을 잊지 마세요.
강조 기능은 독자가 놓치기 쉬운 구체적인 부분에 주의를 환기해주므로 적극적으로 사용하길 권장합니다.
================================================
FILE: LICENSE-DOCS.md
================================================
Attribution 4.0 International
=======================================================================
Creative Commons Corporation ("Creative Commons") is not a law firm and
does not provide legal services or legal advice. Distribution of
Creative Commons public licenses does not create a lawyer-client or
other relationship. Creative Commons makes its licenses and related
information available on an "as-is" basis. Creative Commons gives no
warranties regarding its licenses, any material licensed under their
terms and conditions, or any related information. Creative Commons
disclaims all liability for damages resulting from their use to the
fullest extent possible.
Using Creative Commons Public Licenses
Creative Commons public licenses provide a standard set of terms and
conditions that creators and other rights holders may use to share
original works of authorship and other material subject to copyright
and certain other rights specified in the public license below. The
following considerations are for informational purposes only, are not
exhaustive, and do not form part of our licenses.
Considerations for licensors: Our public licenses are
intended for use by those authorized to give the public
permission to use material in ways otherwise restricted by
copyright and certain other rights. Our licenses are
irrevocable. Licensors should read and understand the terms
and conditions of the license they choose before applying it.
Licensors should also secure all rights necessary before
applying our licenses so that the public can reuse the
material as expected. Licensors should clearly mark any
material not subject to the license. This includes other CC-
licensed material, or material used under an exception or
limitation to copyright. More considerations for licensors:
wiki.creativecommons.org/Considerations_for_licensors
Considerations for the public: By using one of our public
licenses, a licensor grants the public permission to use the
licensed material under specified terms and conditions. If
the licensor's permission is not necessary for any reason--for
example, because of any applicable exception or limitation to
copyright--then that use is not regulated by the license. Our
licenses grant only permissions under copyright and certain
other rights that a licensor has authority to grant. Use of
the licensed material may still be restricted for other
reasons, including because others have copyright or other
rights in the material. A licensor may make special requests,
such as asking that all changes be marked or described.
Although not required by our licenses, you are encouraged to
respect those requests where reasonable. More_considerations
for the public:
wiki.creativecommons.org/Considerations_for_licensees
=======================================================================
Creative Commons Attribution 4.0 International Public License
By exercising the Licensed Rights (defined below), You accept and agree
to be bound by the terms and conditions of this Creative Commons
Attribution 4.0 International Public License ("Public License"). To the
extent this Public License may be interpreted as a contract, You are
granted the Licensed Rights in consideration of Your acceptance of
these terms and conditions, and the Licensor grants You such rights in
consideration of benefits the Licensor receives from making the
Licensed Material available under these terms and conditions.
Section 1 -- Definitions.
a. Adapted Material means material subject to Copyright and Similar
Rights that is derived from or based upon the Licensed Material
and in which the Licensed Material is translated, altered,
arranged, transformed, or otherwise modified in a manner requiring
permission under the Copyright and Similar Rights held by the
Licensor. For purposes of this Public License, where the Licensed
Material is a musical work, performance, or sound recording,
Adapted Material is always produced where the Licensed Material is
synched in timed relation with a moving image.
b. Adapter's License means the license You apply to Your Copyright
and Similar Rights in Your contributions to Adapted Material in
accordance with the terms and conditions of this Public License.
c. Copyright and Similar Rights means copyright and/or similar rights
closely related to copyright including, without limitation,
performance, broadcast, sound recording, and Sui Generis Database
Rights, without regard to how the rights are labeled or
categorized. For purposes of this Public License, the rights
specified in Section 2(b)(1)-(2) are not Copyright and Similar
Rights.
d. Effective Technological Measures means those measures that, in the
absence of proper authority, may not be circumvented under laws
fulfilling obligations under Article 11 of the WIPO Copyright
Treaty adopted on December 20, 1996, and/or similar international
agreements.
e. Exceptions and Limitations means fair use, fair dealing, and/or
any other exception or limitation to Copyright and Similar Rights
that applies to Your use of the Licensed Material.
f. Licensed Material means the artistic or literary work, database,
or other material to which the Licensor applied this Public
License.
g. Licensed Rights means the rights granted to You subject to the
terms and conditions of this Public License, which are limited to
all Copyright and Similar Rights that apply to Your use of the
Licensed Material and that the Licensor has authority to license.
h. Licensor means the individual(s) or entity(ies) granting rights
under this Public License.
i. Share means to provide material to the public by any means or
process that requires permission under the Licensed Rights, such
as reproduction, public display, public performance, distribution,
dissemination, communication, or importation, and to make material
available to the public including in ways that members of the
public may access the material from a place and at a time
individually chosen by them.
j. Sui Generis Database Rights means rights other than copyright
resulting from Directive 96/9/EC of the European Parliament and of
the Council of 11 March 1996 on the legal protection of databases,
as amended and/or succeeded, as well as other essentially
equivalent rights anywhere in the world.
k. You means the individual or entity exercising the Licensed Rights
under this Public License. Your has a corresponding meaning.
Section 2 -- Scope.
a. License grant.
1. Subject to the terms and conditions of this Public License,
the Licensor hereby grants You a worldwide, royalty-free,
non-sublicensable, non-exclusive, irrevocable license to
exercise the Licensed Rights in the Licensed Material to:
a. reproduce and Share the Licensed Material, in whole or
in part; and
b. produce, reproduce, and Share Adapted Material.
2. Exceptions and Limitations. For the avoidance of doubt, where
Exceptions and Limitations apply to Your use, this Public
License does not apply, and You do not need to comply with
its terms and conditions.
3. Term. The term of this Public License is specified in Section
6(a).
4. Media and formats; technical modifications allowed. The
Licensor authorizes You to exercise the Licensed Rights in
all media and formats whether now known or hereafter created,
and to make technical modifications necessary to do so. The
Licensor waives and/or agrees not to assert any right or
authority to forbid You from making technical modifications
necessary to exercise the Licensed Rights, including
technical modifications necessary to circumvent Effective
Technological Measures. For purposes of this Public License,
simply making modifications authorized by this Section 2(a)
(4) never produces Adapted Material.
5. Downstream recipients.
a. Offer from the Licensor -- Licensed Material. Every
recipient of the Licensed Material automatically
receives an offer from the Licensor to exercise the
Licensed Rights under the terms and conditions of this
Public License.
b. No downstream restrictions. You may not offer or impose
any additional or different terms or conditions on, or
apply any Effective Technological Measures to, the
Licensed Material if doing so restricts exercise of the
Licensed Rights by any recipient of the Licensed
Material.
6. No endorsement. Nothing in this Public License constitutes or
may be construed as permission to assert or imply that You
are, or that Your use of the Licensed Material is, connected
with, or sponsored, endorsed, or granted official status by,
the Licensor or others designated to receive attribution as
provided in Section 3(a)(1)(A)(i).
b. Other rights.
1. Moral rights, such as the right of integrity, are not
licensed under this Public License, nor are publicity,
privacy, and/or other similar personality rights; however, to
the extent possible, the Licensor waives and/or agrees not to
assert any such rights held by the Licensor to the limited
extent necessary to allow You to exercise the Licensed
Rights, but not otherwise.
2. Patent and trademark rights are not licensed under this
Public License.
3. To the extent possible, the Licensor waives any right to
collect royalties from You for the exercise of the Licensed
Rights, whether directly or through a collecting society
under any voluntary or waivable statutory or compulsory
licensing scheme. In all other cases the Licensor expressly
reserves any right to collect such royalties.
Section 3 -- License Conditions.
Your exercise of the Licensed Rights is expressly made subject to the
following conditions.
a. Attribution.
1. If You Share the Licensed Material (including in modified
form), You must:
a. retain the following if it is supplied by the Licensor
with the Licensed Material:
i. identification of the creator(s) of the Licensed
Material and any others designated to receive
attribution, in any reasonable manner requested by
the Licensor (including by pseudonym if
designated);
ii. a copyright notice;
iii. a notice that refers to this Public License;
iv. a notice that refers to the disclaimer of
warranties;
v. a URI or hyperlink to the Licensed Material to the
extent reasonably practicable;
b. indicate if You modified the Licensed Material and
retain an indication of any previous modifications; and
c. indicate the Licensed Material is licensed under this
Public License, and include the text of, or the URI or
hyperlink to, this Public License.
2. You may satisfy the conditions in Section 3(a)(1) in any
reasonable manner based on the medium, means, and context in
which You Share the Licensed Material. For example, it may be
reasonable to satisfy the conditions by providing a URI or
hyperlink to a resource that includes the required
information.
3. If requested by the Licensor, You must remove any of the
information required by Section 3(a)(1)(A) to the extent
reasonably practicable.
4. If You Share Adapted Material You produce, the Adapter's
License You apply must not prevent recipients of the Adapted
Material from complying with this Public License.
Section 4 -- Sui Generis Database Rights.
Where the Licensed Rights include Sui Generis Database Rights that
apply to Your use of the Licensed Material:
a. for the avoidance of doubt, Section 2(a)(1) grants You the right
to extract, reuse, reproduce, and Share all or a substantial
portion of the contents of the database;
b. if You include all or a substantial portion of the database
contents in a database in which You have Sui Generis Database
Rights, then the database in which You have Sui Generis Database
Rights (but not its individual contents) is Adapted Material; and
c. You must comply with the conditions in Section 3(a) if You Share
all or a substantial portion of the contents of the database.
For the avoidance of doubt, this Section 4 supplements and does not
replace Your obligations under this Public License where the Licensed
Rights include other Copyright and Similar Rights.
Section 5 -- Disclaimer of Warranties and Limitation of Liability.
a. UNLESS OTHERWISE SEPARATELY UNDERTAKEN BY THE LICENSOR, TO THE
EXTENT POSSIBLE, THE LICENSOR OFFERS THE LICENSED MATERIAL AS-IS
AND AS-AVAILABLE, AND MAKES NO REPRESENTATIONS OR WARRANTIES OF
ANY KIND CONCERNING THE LICENSED MATERIAL, WHETHER EXPRESS,
IMPLIED, STATUTORY, OR OTHER. THIS INCLUDES, WITHOUT LIMITATION,
WARRANTIES OF TITLE, MERCHANTABILITY, FITNESS FOR A PARTICULAR
PURPOSE, NON-INFRINGEMENT, ABSENCE OF LATENT OR OTHER DEFECTS,
ACCURACY, OR THE PRESENCE OR ABSENCE OF ERRORS, WHETHER OR NOT
KNOWN OR DISCOVERABLE. WHERE DISCLAIMERS OF WARRANTIES ARE NOT
ALLOWED IN FULL OR IN PART, THIS DISCLAIMER MAY NOT APPLY TO YOU.
b. TO THE EXTENT POSSIBLE, IN NO EVENT WILL THE LICENSOR BE LIABLE
TO YOU ON ANY LEGAL THEORY (INCLUDING, WITHOUT LIMITATION,
NEGLIGENCE) OR OTHERWISE FOR ANY DIRECT, SPECIAL, INDIRECT,
INCIDENTAL, CONSEQUENTIAL, PUNITIVE, EXEMPLARY, OR OTHER LOSSES,
COSTS, EXPENSES, OR DAMAGES ARISING OUT OF THIS PUBLIC LICENSE OR
USE OF THE LICENSED MATERIAL, EVEN IF THE LICENSOR HAS BEEN
ADVISED OF THE POSSIBILITY OF SUCH LOSSES, COSTS, EXPENSES, OR
DAMAGES. WHERE A LIMITATION OF LIABILITY IS NOT ALLOWED IN FULL OR
IN PART, THIS LIMITATION MAY NOT APPLY TO YOU.
c. The disclaimer of warranties and limitation of liability provided
above shall be interpreted in a manner that, to the extent
possible, most closely approximates an absolute disclaimer and
waiver of all liability.
Section 6 -- Term and Termination.
a. This Public License applies for the term of the Copyright and
Similar Rights licensed here. However, if You fail to comply with
this Public License, then Your rights under this Public License
terminate automatically.
b. Where Your right to use the Licensed Material has terminated under
Section 6(a), it reinstates:
1. automatically as of the date the violation is cured, provided
it is cured within 30 days of Your discovery of the
violation; or
2. upon express reinstatement by the Licensor.
For the avoidance of doubt, this Section 6(b) does not affect any
right the Licensor may have to seek remedies for Your violations
of this Public License.
c. For the avoidance of doubt, the Licensor may also offer the
Licensed Material under separate terms or conditions or stop
distributing the Licensed Material at any time; however, doing so
will not terminate this Public License.
d. Sections 1, 5, 6, 7, and 8 survive termination of this Public
License.
Section 7 -- Other Terms and Conditions.
a. The Licensor shall not be bound by any additional or different
terms or conditions communicated by You unless expressly agreed.
b. Any arrangements, understandings, or agreements regarding the
Licensed Material not stated herein are separate from and
independent of the terms and conditions of this Public License.
Section 8 -- Interpretation.
a. For the avoidance of doubt, this Public License does not, and
shall not be interpreted to, reduce, limit, restrict, or impose
conditions on any use of the Licensed Material that could lawfully
be made without permission under this Public License.
b. To the extent possible, if any provision of this Public License is
deemed unenforceable, it shall be automatically reformed to the
minimum extent necessary to make it enforceable. If the provision
cannot be reformed, it shall be severed from this Public License
without affecting the enforceability of the remaining terms and
conditions.
c. No term or condition of this Public License will be waived and no
failure to comply consented to unless expressly agreed to by the
Licensor.
d. Nothing in this Public License constitutes or may be interpreted
as a limitation upon, or waiver of, any privileges and immunities
that apply to the Licensor or You, including from the legal
processes of any jurisdiction or authority.
=======================================================================
Creative Commons is not a party to its public licenses.
Notwithstanding, Creative Commons may elect to apply one of its public
licenses to material it publishes and in those instances will be
considered the "Licensor." Except for the limited purpose of indicating
that material is shared under a Creative Commons public license or as
otherwise permitted by the Creative Commons policies published at
creativecommons.org/policies, Creative Commons does not authorize the
use of the trademark "Creative Commons" or any other trademark or logo
of Creative Commons without its prior written consent including,
without limitation, in connection with any unauthorized modifications
to any of its public licenses or any other arrangements,
understandings, or agreements concerning use of licensed material. For
the avoidance of doubt, this paragraph does not form part of the public
licenses.
Creative Commons may be contacted at creativecommons.org.
================================================
FILE: README.md
================================================
# ko.react.dev
[](https://discord.gg/YXdTyCh5KF)
## 한국어 번역 정보
### 가이드
번역 혹은 기여를 진행할 때, 아래 가이드를 따라주세요.
1. [기여 가이드라인Contributing](/CONTRIBUTING.md) 및 [기여자 행동 강령 규약Code of Conduct](/CODE_OF_CONDUCT.md)을 따르고 있습니다.
1. [공통 스타일 가이드Universal Style Guide](/wiki/universal-style-guide.md)를 확인해주세요.
1. [번역을 위한 모범 사례Best Practices for Translation](/wiki/best-practices-for-translation.md)를 따라주세요.
1. 공통된 단어 번역을 위해 [번역 용어 정리Translate Glossary](/wiki/translate-glossary.md)를 참고해주세요.
1. 끌어오기 요청Pull Request시 테스트를 통과하지 못할 경우, [`textlint` 가이드Textlint Guide](/wiki/textlint-guide.md)를 참고해주세요.
1. 마지막으로 [맞춤법 검사Spelling Check](https://nara-speller.co.kr/speller/)를 진행해주세요.
이 저장소Repository는 [ko.react.dev](https://ko.react.dev/)의 소스 코드와 개발 문서를 포함하고 있습니다.
## 시작하기
### 사전 준비
1. Git
1. Node: v16.8.0 이상의 모든 버전
1. Yarn v1(`yarn@1.22.22`): [Yarn 설치 안내](https://yarnpkg.com/lang/en/docs/install/) 참고
1. 포크Fork한 개인 저장소
1. 로컬에 클론Clone한 [ko.react.dev 저장소](https://github.com/reactjs/ko.react.dev)
### 설치
1. `cd ko.react.dev`를 실행하여 프로젝트 경로로 이동합니다.
1. `yarn` 명령어를 실행하여 npm 의존성 모듈들을 설치합니다.
### 개발 서버 실행하기
1. `yarn dev` 명령어를 사용하여 개발 서버를 시작합니다. (powered by [Next.js](https://nextjs.org).)
1. `open http://localhost:3000` 명령어를 사용하여 선호하는 브라우저로 접속하세요.
## 기여 방법
### 가이드라인
이 문서는 목적이 다른 여러 섹션으로 나뉩니다. 문장을 추가할 계획이라면, 적절한 섹션에 대한 [기여 가이드라인Contributing](/CONTRIBUTING.md)을 숙지하는 것이 도움이 될 것입니다.
### 분기Branch 만들기
1. `ko.react.dev` 로컬 저장소에서 `git checkout main`을 실행합니다.
1. `git pull origin main`을 실행하여 최신 코드를 가져올 수 있습니다.
1. `git checkout -b the-name-of-my-branch`를 실행하여 분기Branch를 만듭니다. (이때, `the-name-of-my-branch`를 적절한 이름으로 교체.)
### 수정하기
1. ["개발 서버 실행하기"](#개발-서버-실행하기) 부분을 따릅니다.
1. 파일을 저장하고 브라우저에서 확인합니다.
1. `src` 안에 있는 React 컴포넌트가 수정될 경우 hot-reload가 적용됩니다.
1. `content` 안에 있는 마크다운 파일이 수정될 경우 hot-reload가 적용됩니다.
1. 플러그인을 사용하는 경우, `.cache` 디렉토리를 제거한 후 서버를 재시작해야 합니다.
### 수정사항 검사하기
1. 가능하다면, 변경한 부분에 대해서 많이 사용하는 브라우저의 최신 버전에서 시각적으로 제대로 적용되었는지 확인해주세요. (데스크탑과 모바일 모두.)
1. 프로젝트 루트에서 `yarn check-all`을 실행합니다. (이 명령어는 Prettier, ESLint, 그리고 타입 유효성 검사를 진행합니다.)
### 푸시Push 하기
1. `git add -A && git commit -m "My message"`를 실행하여 변경한 파일들을 커밋commit 해주세요. (이때, `My message` 부분을 `Fix header logo on Android` 같은 커밋 메시지로 교체.)
1. `git push my-fork-name the-name-of-my-branch`
1. [ko.react.dev 저장소](https://github.com/reactjs/ko.react.dev)에서 최근에 푸시된 분기Branch를 볼 수 있습니다.
1. 깃허브GitHub 지침을 따라주세요.
1. 가능하다면 시각적으로 변화된 부분의 스크린샷을 첨부해주세요. 변경 사항이 깃허브GitHub에 푸시Push되면 미리보기 빌드가 트리거됩니다.
## 문제 해결하기
`yarn cache-reset` 명령어를 사용하여 로컬 캐시를 초기화합니다.
## 번역
`react.dev` 번역에 흥미가 있다면, [translations.react.dev](https://translations.react.dev/)에서 현재 번역이 얼마나 진행되었는지 확인해주세요.
번역하려는 언어가 아직 진행되지 않았다면, 해당 언어에 대해 새롭게 만들 수 있습니다. [translations.react.dev 저장소](https://github.com/reactjs/translations.react.dev)를 참고해주세요.
## 저작권
위 내용에 대한 저작권은 [react.dev](https://react.dev)가 가지고 있으며, [LICENSE-DOCS.md](/LICENSE-DOCS.md)에서 볼 수 있는 CC-BY-4.0 라이센스를 따릅니다.
================================================
FILE: colors.js
================================================
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
/*
* Copyright (c) Facebook, Inc. and its affiliates.
*/
module.exports = {
// Text colors
primary: '#23272F', // gray-90
'primary-dark': '#F6F7F9', // gray-5
secondary: '#404756', // gray-70
'secondary-dark': '#EBECF0', // gray-10
tertiary: '#5E687E', // gray-50
'tertiary-dark': '#99A1B3', // gray-30
link: '#087EA4', // blue-50
'link-dark': '#58C4DC', // blue-40
syntax: '#EBECF0', // gray-10
wash: '#FFFFFF',
'wash-dark': '#23272F', // gray-90
card: '#F6F7F9', // gray-05
'card-dark': '#343A46', // gray-80
highlight: '#E6F7FF', // blue-10
'highlight-dark': 'rgba(88,175,223,.1)',
border: '#EBECF0', // gray-10
'border-dark': '#343A46', // gray-80
'secondary-button': '#EBECF0', // gray-10
'secondary-button-dark': '#404756', // gray-70
brand: '#087EA4', // blue-40
'brand-dark': '#58C4DC', // blue-40
// Gray
'gray-95': '#16181D',
'gray-90': '#23272F',
'gray-80': '#343A46',
'gray-70': '#404756',
'gray-60': '#4E5769',
'gray-50': '#5E687E',
'gray-40': '#78839B',
'gray-30': '#99A1B3',
'gray-20': '#BCC1CD',
'gray-15': '#D0D3DC',
'gray-10': '#EBECF0',
'gray-5': '#F6F7F9',
// Blue
'blue-80': '#043849',
'blue-60': '#045975',
'blue-50': '#087EA4',
'blue-40': '#149ECA', // Brand Blue
'blue-30': '#58C4DC', // unused
'blue-20': '#ABE2ED',
'blue-10': '#E6F7FF', // todo: doesn't match illustrations
'blue-5': '#E6F6FA',
// Yellow
'yellow-60': '#B65700',
'yellow-50': '#C76A15',
'yellow-40': '#DB7D27', // unused
'yellow-30': '#FABD62', // unused
'yellow-20': '#FCDEB0', // unused
'yellow-10': '#FDE7C7',
'yellow-5': '#FEF5E7',
// Purple
'purple-60': '#2B3491', // unused
'purple-50': '#575FB7',
'purple-40': '#6B75DB',
'purple-30': '#8891EC',
'purple-20': '#C3C8F5', // unused
'purple-10': '#E7E9FB',
'purple-5': '#F3F4FD',
// Green
'green-60': '#2B6E62',
'green-50': '#388F7F',
'green-40': '#44AC99',
'green-30': '#7FCCBF',
'green-20': '#ABDED5',
'green-10': '#E5F5F2',
'green-5': '#F4FBF9',
// RED
'red-60': '#712D28',
'red-50': '#A6423A', // unused
'red-40': '#C1554D',
'red-30': '#D07D77',
'red-20': '#E5B7B3', // unused
'red-10': '#F2DBD9', // unused
'red-5': '#FAF1F0',
// MISC
'code-block': '#99a1b30f', // gray-30 @ 6%
'gradient-blue': '#58C4DC', // Only used for the landing gradient for now.
github: {
highlight: '#fffbdd',
},
};
================================================
FILE: eslint-local-rules/__tests__/fixtures/src/content/basic-error.md
================================================
```jsx
import {useState} from 'react';
function Counter() {
const [count, setCount] = useState(0);
setCount(count + 1);
return
);
}
================================================
FILE: src/components/ErrorDecoderContext.tsx
================================================
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
// Error Decoder requires reading pregenerated error message from getStaticProps,
// but MDX component doesn't support props. So we use React Context to populate
// the value without prop-drilling.
// TODO: Replace with React.cache + React.use when migrating to Next.js App Router
import {createContext, useContext} from 'react';
const notInErrorDecoderContext = Symbol('not in error decoder context');
export const ErrorDecoderContext = createContext<
| {errorMessage: string | null; errorCode: string | null}
| typeof notInErrorDecoderContext
>(notInErrorDecoderContext);
export const useErrorDecoderParams = () => {
const params = useContext(ErrorDecoderContext);
if (params === notInErrorDecoderContext) {
throw new Error('useErrorDecoder must be used in error decoder pages only');
}
return params;
};
================================================
FILE: src/components/ExternalLink.tsx
================================================
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
/*
* Copyright (c) Facebook, Inc. and its affiliates.
*/
import type {DetailedHTMLProps, AnchorHTMLAttributes} from 'react';
export function ExternalLink({
href,
target,
children,
...props
}: DetailedHTMLProps<
AnchorHTMLAttributes,
HTMLAnchorElement
>) {
return (
{children}
);
}
================================================
FILE: src/components/Icon/IconArrow.tsx
================================================
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
/*
* Copyright (c) Facebook, Inc. and its affiliates.
*/
import {memo} from 'react';
import cn from 'classnames';
import type {SVGProps} from 'react';
export const IconArrow = memo<
SVGProps & {
/**
* The direction the arrow should point.
* `start` and `end` are relative to the current locale.
* for example, in LTR, `start` is left and `end` is right.
*/
displayDirection: 'start' | 'end' | 'right' | 'left' | 'up' | 'down';
}
>(function IconArrow({displayDirection, className, ...rest}) {
return (
);
});
================================================
FILE: src/components/Icon/IconArrowSmall.tsx
================================================
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
/*
* Copyright (c) Facebook, Inc. and its affiliates.
*/
import {memo} from 'react';
import cn from 'classnames';
import type {SVGProps} from 'react';
export const IconArrowSmall = memo<
SVGProps & {
/**
* The direction the arrow should point.
* `start` and `end` are relative to the current locale.
* for example, in LTR, `start` is left and `end` is right.
*/
displayDirection: 'start' | 'end' | 'right' | 'left' | 'up' | 'down';
}
>(function IconArrowSmall({displayDirection, className, ...rest}) {
const classes = cn(className, {
'rotate-180': displayDirection === 'left',
'rotate-180 rtl:rotate-0': displayDirection === 'start',
'rtl:rotate-180': displayDirection === 'end',
'rotate-90': displayDirection === 'down',
});
return (
);
});
================================================
FILE: src/components/Icon/IconBsky.tsx
================================================
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
/*
* Copyright (c) Facebook, Inc. and its affiliates.
*/
import {memo} from 'react';
import type {SVGProps} from 'react';
export const IconBsky = memo>(function IconBsky(props) {
return (
);
});
================================================
FILE: src/components/Icon/IconCanary.tsx
================================================
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
/*
* Copyright (c) Facebook, Inc. and its affiliates.
*/
import {memo} from 'react';
export const IconCanary = memo<
JSX.IntrinsicElements['svg'] & {title?: string; size?: 's' | 'md'}
>(function IconCanary(
{className, title, size} = {
className: undefined,
title: undefined,
size: 'md',
}
) {
return (
);
});
================================================
FILE: src/components/Icon/IconChevron.tsx
================================================
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
/*
* Copyright (c) Facebook, Inc. and its affiliates.
*/
import {memo} from 'react';
import cn from 'classnames';
export const IconChevron = memo<
JSX.IntrinsicElements['svg'] & {
/**
* The direction the arrow should point.
* `start` and `end` are relative to the current locale.
* for example, in LTR, `start` is left and `end` is right.
*/
displayDirection: 'start' | 'end' | 'right' | 'left' | 'up' | 'down';
}
>(function IconChevron({className, displayDirection}) {
const classes = cn(
{
'rotate-0': displayDirection === 'down',
'rotate-90': displayDirection === 'left',
'rotate-180': displayDirection === 'up',
'-rotate-90': displayDirection === 'right',
'rotate-90 rtl:-rotate-90': displayDirection === 'start',
'-rotate-90 rtl:rotate-90': displayDirection === 'end',
},
className
);
return (
);
});
================================================
FILE: src/components/Icon/IconClose.tsx
================================================
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
/*
* Copyright (c) Facebook, Inc. and its affiliates.
*/
import {memo} from 'react';
import type {SVGProps} from 'react';
export const IconClose = memo>(function IconClose(
props
) {
return (
);
});
================================================
FILE: src/components/Icon/IconCodeBlock.tsx
================================================
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
/*
* Copyright (c) Facebook, Inc. and its affiliates.
*/
import {memo} from 'react';
export const IconCodeBlock = memo(
function IconCodeBlock({className}) {
return (
);
}
);
================================================
FILE: src/components/Icon/IconCopy.tsx
================================================
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
/*
* Copyright (c) Facebook, Inc. and its affiliates.
*/
import {memo} from 'react';
export const IconCopy = memo(function IconCopy({
className,
}) {
return (
);
});
================================================
FILE: src/components/Icon/IconDeepDive.tsx
================================================
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
/*
* Copyright (c) Facebook, Inc. and its affiliates.
*/
import {memo} from 'react';
export const IconDeepDive = memo(
function IconDeepDive({className}) {
return (
);
}
);
================================================
FILE: src/components/Icon/IconDownload.tsx
================================================
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
/*
* Copyright (c) Facebook, Inc. and its affiliates.
*/
import {memo} from 'react';
export const IconDownload = memo(
function IconDownload({className}) {
return (
);
}
);
================================================
FILE: src/components/Icon/IconError.tsx
================================================
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
/*
* Copyright (c) Facebook, Inc. and its affiliates.
*/
import {memo} from 'react';
export const IconError = memo(function IconError({
className,
}) {
return (
);
});
================================================
FILE: src/components/Icon/IconExperimental.tsx
================================================
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
/*
* Copyright (c) Facebook, Inc. and its affiliates.
*/
import {memo} from 'react';
export const IconExperimental = memo<
JSX.IntrinsicElements['svg'] & {title?: string; size?: 's' | 'md'}
>(function IconExperimental(
{className, title, size} = {
className: undefined,
title: undefined,
size: 'md',
}
) {
return (
);
});
================================================
FILE: src/components/Icon/IconFacebookCircle.tsx
================================================
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
/*
* Copyright (c) Facebook, Inc. and its affiliates.
*/
import {memo} from 'react';
import type {SVGProps} from 'react';
export const IconFacebookCircle = memo>(
function IconFacebookCircle(props) {
return (
);
}
);
================================================
FILE: src/components/Icon/IconGitHub.tsx
================================================
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
/*
* Copyright (c) Facebook, Inc. and its affiliates.
*/
import {memo} from 'react';
import type {SVGProps} from 'react';
export const IconGitHub = memo>(function IconGitHub(
props
) {
return (
);
});
================================================
FILE: src/components/Icon/IconHamburger.tsx
================================================
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
/*
* Copyright (c) Facebook, Inc. and its affiliates.
*/
import {memo} from 'react';
import type {SVGProps} from 'react';
export const IconHamburger = memo>(
function IconHamburger(props) {
return (
);
}
);
================================================
FILE: src/components/Icon/IconHint.tsx
================================================
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
/*
* Copyright (c) Facebook, Inc. and its affiliates.
*/
import {memo} from 'react';
import cn from 'classnames';
export const IconHint = memo(function IconHint({
className,
}) {
return (
);
});
================================================
FILE: src/components/Icon/IconInstagram.tsx
================================================
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
/*
* Copyright (c) Facebook, Inc. and its affiliates.
*/
import {memo} from 'react';
import type {SVGProps} from 'react';
export const IconInstagram = memo>(
function IconInstagram(props) {
return (
);
}
);
================================================
FILE: src/components/Icon/IconLink.tsx
================================================
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
/*
* Copyright (c) Facebook, Inc. and its affiliates.
*/
import {memo} from 'react';
import type {SVGProps} from 'react';
export const IconLink = memo>(function IconLink(props) {
return (
);
});
================================================
FILE: src/components/Icon/IconNavArrow.tsx
================================================
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
/*
* Copyright (c) Facebook, Inc. and its affiliates.
*/
import {memo} from 'react';
import cn from 'classnames';
export const IconNavArrow = memo<
JSX.IntrinsicElements['svg'] & {
/**
* The direction the arrow should point.
* `start` and `end` are relative to the current locale.
* for example, in LTR, `start` is left and `end` is right.
*/
displayDirection: 'start' | 'end' | 'right' | 'left' | 'down';
}
>(function IconNavArrow({displayDirection = 'start', className}) {
const classes = cn(
'duration-100 ease-in transition',
{
'rotate-0': displayDirection === 'down',
'rotate-90': displayDirection === 'left',
'-rotate-90': displayDirection === 'right',
'rotate-90 rtl:-rotate-90': displayDirection === 'start',
'-rotate-90 rtl:rotate-90': displayDirection === 'end',
},
className
);
return (
);
});
================================================
FILE: src/components/Icon/IconNewPage.tsx
================================================
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
/*
* Copyright (c) Facebook, Inc. and its affiliates.
*/
import {memo} from 'react';
import type {SVGProps} from 'react';
export const IconNewPage = memo>(function IconNewPage(
props
) {
return (
);
});
================================================
FILE: src/components/Icon/IconNote.tsx
================================================
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
/*
* Copyright (c) Facebook, Inc. and its affiliates.
*/
import {memo} from 'react';
export const IconNote = memo(function IconNote({
className,
}) {
return (
);
});
================================================
FILE: src/components/Icon/IconPitfall.tsx
================================================
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
/*
* Copyright (c) Facebook, Inc. and its affiliates.
*/
import {memo} from 'react';
export const IconPitfall = memo(
function IconPitfall({className}) {
return (
);
}
);
================================================
FILE: src/components/Icon/IconRestart.tsx
================================================
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
/*
* Copyright (c) Facebook, Inc. and its affiliates.
*/
import {memo} from 'react';
export const IconRestart = memo(
function IconRestart({className}) {
return (
);
}
);
================================================
FILE: src/components/Icon/IconRocket.tsx
================================================
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
/*
* Copyright (c) Facebook, Inc. and its affiliates.
*/
import {memo} from 'react';
export const IconRocket = memo<
JSX.IntrinsicElements['svg'] & {title?: string; size?: 's' | 'md'}
>(function IconRocket({className, size = 'md'}) {
return (
);
});
================================================
FILE: src/components/Icon/IconRss.tsx
================================================
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
/*
* Copyright (c) Facebook, Inc. and its affiliates.
*/
import {memo} from 'react';
import type {SVGProps} from 'react';
export const IconRss = memo>(function IconRss(props) {
return (
);
});
================================================
FILE: src/components/Icon/IconSearch.tsx
================================================
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
/*
* Copyright (c) Facebook, Inc. and its affiliates.
*/
import {memo} from 'react';
import type {SVGProps} from 'react';
export const IconSearch = memo>(function IconSearch(
props
) {
return (
);
});
================================================
FILE: src/components/Icon/IconSolution.tsx
================================================
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
/*
* Copyright (c) Facebook, Inc. and its affiliates.
*/
import {memo} from 'react';
import cn from 'classnames';
export const IconSolution = memo(
function IconSolution({className}) {
return (
);
}
);
================================================
FILE: src/components/Icon/IconTerminal.tsx
================================================
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
/*
* Copyright (c) Facebook, Inc. and its affiliates.
*/
import {memo} from 'react';
export const IconTerminal = memo(
function IconTerminal({className}) {
return (
);
}
);
================================================
FILE: src/components/Icon/IconThreads.tsx
================================================
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
/*
* Copyright (c) Facebook, Inc. and its affiliates.
*/
import {memo} from 'react';
import type {SVGProps} from 'react';
export const IconThreads = memo>(function IconThreads(
props
) {
return (
);
});
================================================
FILE: src/components/Icon/IconTwitter.tsx
================================================
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
/*
* Copyright (c) Facebook, Inc. and its affiliates.
*/
import {memo} from 'react';
import type {SVGProps} from 'react';
export const IconTwitter = memo>(function IconTwitter(
props
) {
return (
);
});
================================================
FILE: src/components/Icon/IconWarning.tsx
================================================
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
/*
* Copyright (c) Facebook, Inc. and its affiliates.
*/
import {memo} from 'react';
export const IconWarning = memo(
function IconWarning({className}) {
return (
);
}
);
================================================
FILE: src/components/Layout/Feedback.tsx
================================================
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
/*
* Copyright (c) Facebook, Inc. and its affiliates.
*/
import {useState} from 'react';
import {useRouter} from 'next/router';
import cn from 'classnames';
export function Feedback({onSubmit = () => {}}: {onSubmit?: () => void}) {
const {asPath} = useRouter();
const cleanedPath = asPath.split(/[\?\#]/)[0];
// Reset on route changes.
return ;
}
const thumbsUpIcon = (
);
const thumbsDownIcon = (
);
function sendGAEvent(isPositive: boolean) {
const category = isPositive ? 'like_button' : 'dislike_button';
const value = isPositive ? 1 : 0;
// Fragile. Don't change unless you've tested the network payload
// and verified that the right events actually show up in GA.
// @ts-ignore
gtag('event', 'feedback', {
event_category: category,
event_label: window.location.pathname,
event_value: value,
});
}
function SendFeedback({onSubmit}: {onSubmit: () => void}) {
const [isSubmitted, setIsSubmitted] = useState(false);
return (
{isSubmitted
? '피드백을 보내주셔서 감사합니다!'
: '이 페이지가 도움이 되었나요?'}
{!isSubmitted && (
)}
{!isSubmitted && (
)}
);
}
================================================
FILE: src/components/Layout/Footer.tsx
================================================
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
/*
* Copyright (c) Facebook, Inc. and its affiliates.
*/
import * as React from 'react';
import NextLink from 'next/link';
import cn from 'classnames';
import {ExternalLink} from 'components/ExternalLink';
import {IconFacebookCircle} from 'components/Icon/IconFacebookCircle';
import {IconTwitter} from 'components/Icon/IconTwitter';
import {IconBsky} from 'components/Icon/IconBsky';
import {IconGitHub} from 'components/Icon/IconGitHub';
export function Footer() {
const socialLinkClasses = 'hover:text-primary dark:text-primary-dark';
return (
);
}
function FooterLink({
href,
children,
isHeader = false,
}: {
href?: string;
children: React.ReactNode;
isHeader?: boolean;
}) {
const classes = cn('border-b inline-block border-transparent', {
'text-sm text-primary dark:text-primary-dark': !isHeader,
'text-md text-secondary dark:text-secondary-dark my-2 font-bold': isHeader,
'hover:border-gray-10': href,
});
if (!href) {
return
{children}
;
}
if (href.startsWith('https://')) {
return (
{children}
);
}
return (
{children}
);
}
================================================
FILE: src/components/Layout/HomeContent.js
================================================
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
/*
* Copyright (c) Facebook, Inc. and its affiliates.
*/
import {
createContext,
memo,
useState,
useContext,
useId,
Suspense,
useEffect,
useRef,
useTransition,
} from 'react';
import cn from 'classnames';
import NextLink from 'next/link';
import ButtonLink from '../ButtonLink';
import {IconRestart} from '../Icon/IconRestart';
import BlogCard from 'components/MDX/BlogCard';
import {IconChevron} from 'components/Icon/IconChevron';
import {IconSearch} from 'components/Icon/IconSearch';
import {Logo} from 'components/Logo';
import Link from 'components/MDX/Link';
import CodeBlock from 'components/MDX/CodeBlock';
import {ExternalLink} from 'components/ExternalLink';
import sidebarBlog from '../../sidebarBlog.json';
import * as React from 'react';
import Image from 'next/image';
function Section({children, background = null}) {
return (
컴포넌트를 사용하여
사용자 인터페이스 만들기
React를 사용하면 컴포넌트라 불리는 조각들을 사용하여 사용자
인터페이스를 만들 수 있습니다. Thumbnail,{' '}
LikeButton, 그리고 Video 같은 컴포넌트를
만들어 보세요. 그런 다음 전체 화면, 페이지 및 앱에서 이들을 결합할
수 있습니다.
혼자서 작업하든, 수천 명의 다른 개발자와 함께 작업하든, React를
사용하는 느낌은 동일합니다. 개인, 팀, 조직에서 작성한 컴포넌트를
원활하게 결합할 수 있도록 설계하였습니다.
코드와 마크업으로
컴포넌트 작성하기
React 컴포넌트는 자바스크립트 함수입니다. 조건부로 내용을
표시하려면 if 문을 사용할 수 있습니다. 목록을
표시하려면 배열에 map()을 사용할 수 있습니다. React를
배우는 것은 프로그래밍을 배우는 것입니다.
이 마크업 구문을 JSX라 부릅니다. 이것은 React에 의해서 대중화된
자바스크립트 구문의 확장입니다. JSX 마크업을 관련된 렌더링 로직과
가까이 두면, React 컴포넌트를 쉽게 만들고 관리하고 삭제할 수
있습니다.
필요한 곳에
상호작용 요소 추가하기
React 컴포넌트는 데이터를 받고 화면에 표시할 내용을 반환합니다.
사용자가 입력란에 입력하는 것과 같이 상호작용에 응답하여 새
데이터를 전달할 수 있습니다. 그런 다음 React는 새 데이터와
일치하도록 화면을 업데이트합니다.
전체 페이지를 React로 빌드할 필요는 없습니다. React를 기존 HTML
페이지에 추가하고, 페이지 어디에서나 상호작용하는 React 컴포넌트를
렌더링할 수 있습니다.
페이지에 React 추가하기
프레임워크를 통해
풀스택으로 만들기
React는 라이브러리입니다. 컴포넌트를 조합할 수 있도록 도와주지만,
라우팅이나 데이터를 가져오는 방법을 규정하지는 않습니다. React로
완전한 앱을 만들려면,{' '}
Next.js 또는{' '}
React Router 같은
풀스택 React 프레임워크를 추천합니다.
React는 아키텍처이기도 합니다. 이를 구현하는 프레임워크는 서버에서
실행되는 비동기 컴포넌트 혹은 빌드 중에 실행되는 비동기
컴포넌트에서 데이터를 가져올 수 있도록 합니다. 파일이나
데이터베이스에서 데이터를 읽고, 이를 상호작용하는 컴포넌트에
전달할 수 있습니다.
프레임워크로 시작하기
모든 플랫폼에서 사용하기
사람들은 다양한 이유로 웹과 네이티브 앱을 좋아합니다. React는
동일한 기술을 사용하여 웹 앱과 네이티브 앱을 모두 만들 수
있습니다. 각 플랫폼의 장점을 활용하여 모든 플랫폼에서 적합한
인터페이스를 구현할 수 있습니다.
웹에 충실하기
사람들은 웹 앱이 빠르게 로드되길 기대합니다. 서버에서
React를 사용하면 데이터를 가져오는 동안 HTML을
스트리밍하여 자바스크립트 코드가 로드되기 전에 남은
내용을 점진적으로 채울 수 있습니다. 클라이언트에서
React는 표준 웹 API를 사용하여 렌더링 중에도 UI가
반응하도록 유지할 수 있습니다.
진정한 네이티브에서 사용하기
사람들은 네이티브Native 앱이 해당 플랫폼의
모습과 느낌을 갖기를 기대합니다.{' '}
React Native
와{' '}
Expo
를 사용하면 React를 통해 Android, iOS 등을 위한 앱을
빌드할 수 있습니다. UI가 진정한 네이티브이기 때문에
네이티브처럼 보이고 느껴집니다. 이것은 웹 뷰
Web View가 아닙니다. React 컴포넌트들은
실제 Android, iOS 플랫폼에서 제공하는 뷰
View를 렌더링합니다.
React를 사용하면 웹 및 네이티브 개발자가 될 수 있습니다. 사용자
경험의 저하 없이 여러 플랫폼에 출시할 수 있습니다. 조직에서는
플랫폼 간의 격차를 줄이고, 기능을 완전히 소유하는 팀을 구성할 수
있습니다.
네이티브 플랫폼에서 React 사용하기
새로운 기능에 맞춰
업그레이드 하기
React는 변화에 신중하게 접근합니다. 모든 React 커밋은 10억명
이상의 사용자가 있는 비즈니스의 크리티컬한 영역에서 테스트를
진행합니다. Meta에서는 10만 개 이상의 React 컴포넌트가 모든
마이그레이션 전략을 검증합니다.
React 팀은 항상 React를 개선하는 방법을 연구합니다. 몇 년이
걸리는 연구도 있습니다. React는 연구 아이디어를 제품에
적용하는 데에 높은 기준을 가지고 있습니다. 검증된 접근
방식만이 React 일부가 됩니다.
더 많은 React 뉴스 읽기
최신 React 뉴스
React 뉴스 더 보기
수백만 명이 있는 커뮤니티
여러분은 혼자가 아닙니다. 전세계의 200만 명이 넘는 개발자들이
React 문서를 매달 방문합니다. React는 사람들과 팀이 동의할 수
있는 것입니다.
이것이 바로 React가 단순한 라이브러리, 아키텍처, 혹은 생태계
그 이상인 이유입니다. React는 바로 커뮤니티입니다. 도움을
요청하고, 기회를 찾고, 새로운 친구를 만날 수 있는 곳입니다.
개발자와 디자이너, 초보자와 전문가, 연구원과 예술가, 교사와
학생을 만날 수 있습니다. 배경은 모두 다를 수 있지만, React를
통해 함께 사용자 인터페이스를 만들 수 있습니다.
React 커뮤니티에
오신 것을 환영합니다
시작하기
>
);
}
function CTA({children, icon, href}) {
let Tag;
let extraProps;
if (href.startsWith('https://')) {
Tag = ExternalLink;
} else {
Tag = NextLink;
extraProps = {legacyBehavior: false};
}
return (
{icon === 'native' && (
)}
{icon === 'framework' && (
)}
{icon === 'code' && (
)}
{icon === 'news' && (
)}
{children}
);
}
const reactConf2021Cover = '/images/home/conf2021/cover.svg';
const reactConf2019Cover = '/images/home/conf2019/cover.svg';
const communityImages = [
{
src: '/images/home/community/react_conf_fun.webp',
alt: 'People singing karaoke at React Conf',
},
{
src: '/images/home/community/react_india_sunil.webp',
alt: 'Sunil Pai speaking at React India',
},
{
src: '/images/home/community/react_conf_hallway.webp',
alt: 'A hallway conversation between two people at React Conf',
},
{
src: '/images/home/community/react_india_hallway.webp',
alt: 'A hallway conversation at React India',
},
{
src: '/images/home/community/react_conf_elizabet.webp',
alt: 'Elizabet Oliveira speaking at React Conf',
},
{
src: '/images/home/community/react_india_selfie.webp',
alt: 'People taking a group selfie at React India',
},
{
src: '/images/home/community/react_conf_nat.webp',
alt: 'Nat Alison speaking at React Conf',
},
{
src: '/images/home/community/react_india_team.webp',
alt: 'Organizers greeting attendees at React India',
},
];
function CommunityGallery() {
const ref = useRef();
const [shouldPlay, setShouldPlay] = useState(false);
useEffect(() => {
const observer = new IntersectionObserver(
(entries) => {
entries.forEach((entry) => {
setShouldPlay(entry.isIntersecting);
});
},
{
root: null,
rootMargin: `${window.innerHeight}px 0px`,
}
);
observer.observe(ref.current);
return () => observer.disconnect();
}, []);
const [isLazy, setIsLazy] = useState(true);
// Either wait until we're scrolling close...
useEffect(() => {
if (!isLazy) {
return;
}
const rootVertical = parseInt(window.innerHeight * 2.5);
const observer = new IntersectionObserver(
(entries) => {
entries.forEach((entry) => {
if (entry.isIntersecting) {
setIsLazy(false);
}
});
},
{
root: null,
rootMargin: `${rootVertical}px 0px`,
}
);
observer.observe(ref.current);
return () => observer.disconnect();
}, [isLazy]);
// ... or until it's been a while after hydration.
useEffect(() => {
const timeout = setTimeout(() => {
setIsLazy(false);
}, 20 * 1000);
return () => clearTimeout(timeout);
}, []);
return (
)}
{/* No fallback UI so need to be careful not to suspend directly inside. */}
{content}
{!isHomePage && (
)}
{showToc && toc.length > 0 && }
>
);
}
================================================
FILE: src/components/Layout/Sidebar/SidebarButton.tsx
================================================
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
/*
* Copyright (c) Facebook, Inc. and its affiliates.
*/
import * as React from 'react';
import cn from 'classnames';
import {IconNavArrow} from 'components/Icon/IconNavArrow';
interface SidebarButtonProps {
title: string;
heading: boolean;
level: number;
onClick: (event: React.MouseEvent) => void;
isExpanded?: boolean;
isBreadcrumb?: boolean;
}
export function SidebarButton({
title,
heading,
level,
onClick,
isExpanded,
isBreadcrumb,
}: SidebarButtonProps) {
return (
1,
})}>
);
}
================================================
FILE: src/components/Layout/Sidebar/SidebarLink.tsx
================================================
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
/*
* Copyright (c) Facebook, Inc. and its affiliates.
*/
/* eslint-disable jsx-a11y/no-static-element-interactions */
/* eslint-disable jsx-a11y/click-events-have-key-events */
import {useRef, useEffect} from 'react';
import * as React from 'react';
import cn from 'classnames';
import {IconNavArrow} from 'components/Icon/IconNavArrow';
import {IconCanary} from 'components/Icon/IconCanary';
import {IconExperimental} from 'components/Icon/IconExperimental';
import Link from 'next/link';
interface SidebarLinkProps {
href: string;
selected?: boolean;
title: string;
level: number;
version?: 'canary' | 'major' | 'experimental' | 'rc';
icon?: React.ReactNode;
isExpanded?: boolean;
hideArrow?: boolean;
isPending: boolean;
}
export function SidebarLink({
href,
selected = false,
title,
version,
level,
isExpanded,
hideArrow,
isPending,
}: SidebarLinkProps) {
const ref = useRef(null);
useEffect(() => {
if (selected && ref && ref.current) {
// @ts-ignore
if (typeof ref.current.scrollIntoViewIfNeeded === 'function') {
// @ts-ignore
ref.current.scrollIntoViewIfNeeded();
}
}
}, [ref, selected]);
let target = '';
if (href.startsWith('https://')) {
target = '_blank';
}
return (
0,
'ps-5': level < 2,
'text-base font-bold': level === 0,
'text-primary dark:text-primary-dark': level === 0 && !selected,
'text-base text-secondary dark:text-secondary-dark':
level > 0 && !selected,
'text-base text-link dark:text-link-dark bg-highlight dark:bg-highlight-dark border-blue-40 hover:bg-highlight hover:text-link dark:hover:bg-highlight-dark dark:hover:text-link-dark':
selected,
'dark:bg-gray-70 bg-gray-3 dark:hover:bg-gray-70 hover:bg-gray-3':
isPending,
}
)}>
{/* This here needs to be refactored ofc */}
{currentRoutes.map(
(
{
path,
title,
routes,
version,
heading,
hasSectionHeader,
sectionHeader,
},
index
) => {
const selected = slug === path;
let listItem = null;
if (!path || heading) {
// if current route item has no path and children treat it as an API sidebar heading
listItem = (
);
} else if (routes) {
// if route has a path and child routes, treat it as an expandable sidebar item
const isBreadcrumb =
breadcrumbs.length > 1 &&
breadcrumbs[breadcrumbs.length - 1].path === path;
const isExpanded = isForceExpanded || isBreadcrumb || selected;
listItem = (
);
} else {
// if route has a path and no child routes, treat it as a sidebar link
listItem = (
);
}
================================================
FILE: src/components/Layout/Sidebar/index.tsx
================================================
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
/*
* Copyright (c) Facebook, Inc. and its affiliates.
*/
export {SidebarButton} from './SidebarButton';
export {SidebarLink} from './SidebarLink';
export {SidebarRouteTree} from './SidebarRouteTree';
================================================
FILE: src/components/Layout/SidebarNav/SidebarNav.tsx
================================================
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
/*
* Copyright (c) Facebook, Inc. and its affiliates.
*/
import {Suspense} from 'react';
import * as React from 'react';
import cn from 'classnames';
import {SidebarRouteTree} from '../Sidebar/SidebarRouteTree';
import type {RouteItem} from '../getRouteMeta';
declare global {
interface Window {
__theme: string;
__setPreferredTheme: (theme: string) => void;
}
}
export default function SidebarNav({
routeTree,
breadcrumbs,
}: {
routeTree: RouteItem;
breadcrumbs: RouteItem[];
}) {
// HACK. Fix up the data structures instead.
if ((routeTree as any).routes.length === 1) {
routeTree = (routeTree as any).routes[0];
}
return (
);
}
================================================
FILE: src/components/Layout/SidebarNav/index.tsx
================================================
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
/*
* Copyright (c) Facebook, Inc. and its affiliates.
*/
export {default as SidebarNav} from './SidebarNav';
================================================
FILE: src/components/Layout/Toc.tsx
================================================
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
/*
* Copyright (c) Facebook, Inc. and its affiliates.
*/
import cx from 'classnames';
import {useTocHighlight} from './useTocHighlight';
import type {Toc} from '../MDX/TocContext';
export function Toc({headings}: {headings: Toc}) {
const {currentIndex} = useTocHighlight();
// TODO: We currently have a mismatch between the headings in the document
// and the headings we find in MarkdownPage (i.e. we don't find Recap or Challenges).
// Select the max TOC item we have here for now, but remove this after the fix.
const selectedIndex = Math.min(currentIndex, headings.length - 1);
return (
);
}
================================================
FILE: src/components/Layout/TopNav/BrandMenu.tsx
================================================
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
import * as ContextMenu from '@radix-ui/react-context-menu';
import {IconCopy} from 'components/Icon/IconCopy';
import {IconDownload} from 'components/Icon/IconDownload';
import {IconNewPage} from 'components/Icon/IconNewPage';
import {ExternalLink} from 'components/ExternalLink';
import {IconClose} from '../../Icon/IconClose';
function MenuItem({
children,
onSelect,
}: {
children: React.ReactNode;
onSelect?: () => void;
}) {
return (
{children}
);
}
function DownloadMenuItem({
fileName,
href,
children,
}: {
fileName: string;
href: string;
children: React.ReactNode;
}) {
return (
);
}
export default function BrandMenu({children}: {children: React.ReactNode}) {
return (
{children}
Dark Mode
Logo SVGWordmark SVG
Light Mode
Logo SVGWordmark SVG
uwu
Logo PNG
);
}
================================================
FILE: src/components/Layout/TopNav/TopNav.tsx
================================================
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
/*
* Copyright (c) Facebook, Inc. and its affiliates.
*/
import {
useState,
useRef,
useCallback,
useEffect,
startTransition,
Suspense,
} from 'react';
import Image from 'next/image';
import * as React from 'react';
import cn from 'classnames';
import NextLink from 'next/link';
import {useRouter} from 'next/router';
import {disableBodyScroll, enableBodyScroll} from 'body-scroll-lock';
import {IconClose} from 'components/Icon/IconClose';
import {IconHamburger} from 'components/Icon/IconHamburger';
import {IconSearch} from 'components/Icon/IconSearch';
import {Search} from 'components/Search';
import {Logo} from '../../Logo';
import {SidebarRouteTree} from '../Sidebar';
import type {RouteItem} from '../getRouteMeta';
import {siteConfig} from 'siteConfig';
import BrandMenu from './BrandMenu';
declare global {
interface Window {
__theme: string;
__setPreferredTheme: (theme: string) => void;
}
}
const darkIcon = (
);
const lightIcon = (
);
const languageIcon = (
);
const githubIcon = (
);
function Link({
href,
children,
...props
}: React.AnchorHTMLAttributes) {
return (
{children}
);
}
function NavItem({url, isActive, children}: any) {
return (
{children}
);
}
function Kbd(props: {children?: React.ReactNode; wide?: boolean}) {
const {wide, ...rest} = props;
const width = wide ? 'w-10' : 'w-5';
return (
);
}
export default function TopNav({
routeTree,
breadcrumbs,
section,
}: {
routeTree: RouteItem;
breadcrumbs: RouteItem[];
section: 'learn' | 'reference' | 'community' | 'blog' | 'home' | 'unknown';
}) {
const [isMenuOpen, setIsMenuOpen] = useState(false);
const [showSearch, setShowSearch] = useState(false);
const [isScrolled, setIsScrolled] = useState(false);
const scrollParentRef = useRef(null);
const {asPath} = useRouter();
// HACK. Fix up the data structures instead.
if ((routeTree as any).routes.length === 1) {
routeTree = (routeTree as any).routes[0];
}
// While the overlay is open, disable body scroll.
useEffect(() => {
if (isMenuOpen) {
const preferredScrollParent = scrollParentRef.current!;
disableBodyScroll(preferredScrollParent);
return () => enableBodyScroll(preferredScrollParent);
} else {
return undefined;
}
}, [isMenuOpen]);
// Close the overlay on any navigation.
useEffect(() => {
setIsMenuOpen(false);
}, [asPath]);
// Also close the overlay if the window gets resized past mobile layout.
// (This is also important because we don't want to keep the body locked!)
useEffect(() => {
const media = window.matchMedia(`(max-width: 1023px)`);
function closeIfNeeded() {
if (!media.matches) {
setIsMenuOpen(false);
}
}
closeIfNeeded();
media.addEventListener('change', closeIfNeeded);
return () => {
media.removeEventListener('change', closeIfNeeded);
};
}, []);
const scrollDetectorRef = useRef(null);
useEffect(() => {
const observer = new IntersectionObserver(
(entries) => {
entries.forEach((entry) => {
setIsScrolled(!entry.isIntersecting);
});
},
{
root: null,
rootMargin: `0px 0px`,
threshold: 0,
}
);
observer.observe(scrollDetectorRef.current!);
return () => observer.disconnect();
}, []);
const onOpenSearch = useCallback(() => {
startTransition(() => {
setShowSearch(true);
});
}, []);
const onCloseSearch = useCallback(() => {
setShowSearch(false);
}, []);
return (
<>
{isMenuOpen && (
)}
>
);
}
================================================
FILE: src/components/Layout/TopNav/index.tsx
================================================
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
/*
* Copyright (c) Facebook, Inc. and its affiliates.
*/
export {default as TopNav} from './TopNav';
================================================
FILE: src/components/Layout/getRouteMeta.tsx
================================================
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
/*
* Copyright (c) Facebook, Inc. and its affiliates.
*/
/**
* While Next.js provides file-based routing, we still need to construct
* a sidebar for navigation and provide each markdown page
* previous/next links and titles. To do this, we construct a nested
* route object that is infinitely nestable.
*/
export type RouteTag =
| 'foundation'
| 'intermediate'
| 'advanced'
| 'experimental'
| 'deprecated';
export interface RouteItem {
/** Page title (for the sidebar) */
title: string;
/** Optional version flag for heading */
version?: 'canary' | 'major';
/** Optional page description for heading */
description?: string;
/* Additional meta info for page tagging */
tags?: RouteTag[];
/** Path to page */
path?: string;
/** Whether the entry is a heading */
heading?: boolean;
/** List of sub-routes */
routes?: RouteItem[];
/** Adds a section header above the route item */
hasSectionHeader?: boolean;
/** Title of section header */
sectionHeader?: string;
/** Whether it should be omitted in breadcrumbs */
skipBreadcrumb?: boolean;
}
export interface Routes {
/** List of routes */
routes: RouteItem[];
}
/** Routing metadata about a given route and it's siblings and parent */
export interface RouteMeta {
/** The previous route */
prevRoute?: RouteItem;
/** The next route */
nextRoute?: RouteItem;
/** The current route */
route?: RouteItem;
/** Trail of parent routes */
breadcrumbs?: RouteItem[];
/** Order in the section */
order?: number;
}
type TraversalContext = RouteMeta & {
currentIndex: number;
};
export function getRouteMeta(cleanedPath: string, routeTree: RouteItem) {
const breadcrumbs = getBreadcrumbs(cleanedPath, routeTree);
const ctx: TraversalContext = {
currentIndex: 0,
};
buildRouteMeta(cleanedPath, routeTree, ctx);
const {currentIndex: _, ...meta} = ctx;
return {
...meta,
breadcrumbs: breadcrumbs.length > 0 ? breadcrumbs : [routeTree],
};
}
// Performs a depth-first search to find the current route and its previous/next route
function buildRouteMeta(
searchPath: string,
currentRoute: RouteItem,
ctx: TraversalContext
) {
ctx.currentIndex++;
const {routes} = currentRoute;
if (ctx.route && !ctx.nextRoute) {
ctx.nextRoute = currentRoute;
}
if (currentRoute.path === searchPath) {
ctx.route = currentRoute;
ctx.order = ctx.currentIndex;
// If we've found a deeper match, reset the previously stored next route.
// TODO: this only works reliably if deeper matches are first in the tree.
// We should revamp all of this to be more explicit.
ctx.nextRoute = undefined;
}
if (!ctx.route) {
ctx.prevRoute = currentRoute;
}
if (!routes) {
return;
}
for (const route of routes) {
buildRouteMeta(searchPath, route, ctx);
}
}
// iterates the route tree from the current route to find its ancestors for breadcrumbs
function getBreadcrumbs(
path: string,
currentRoute: RouteItem,
breadcrumbs: RouteItem[] = []
): RouteItem[] {
if (currentRoute.path === path) {
return breadcrumbs;
}
if (!currentRoute.routes) {
return [];
}
for (const route of currentRoute.routes) {
const childRoute = getBreadcrumbs(path, route, [
...breadcrumbs,
currentRoute,
]);
if (childRoute?.length) {
return childRoute;
}
}
return [];
}
================================================
FILE: src/components/Layout/useTocHighlight.tsx
================================================
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
/*
* Copyright (c) Facebook, Inc. and its affiliates.
*/
import {useState, useRef, useEffect} from 'react';
const TOP_OFFSET = 85;
export function getHeaderAnchors(): HTMLAnchorElement[] {
return Array.prototype.filter.call(
document.getElementsByClassName('mdx-header-anchor'),
function (testElement) {
return (
testElement.parentNode.nodeName === 'H1' ||
testElement.parentNode.nodeName === 'H2' ||
testElement.parentNode.nodeName === 'H3'
);
}
);
}
/**
* Sets up Table of Contents highlighting.
*/
export function useTocHighlight() {
const [currentIndex, setCurrentIndex] = useState(0);
const timeoutRef = useRef(null);
useEffect(() => {
function updateActiveLink() {
const pageHeight = document.body.scrollHeight;
const scrollPosition = window.scrollY + window.innerHeight;
const headersAnchors = getHeaderAnchors();
if (scrollPosition >= 0 && pageHeight - scrollPosition <= 0) {
// Scrolled to bottom of page.
setCurrentIndex(headersAnchors.length - 1);
return;
}
let index = -1;
while (index < headersAnchors.length - 1) {
const headerAnchor = headersAnchors[index + 1];
const {top} = headerAnchor.getBoundingClientRect();
if (top >= TOP_OFFSET) {
break;
}
index += 1;
}
setCurrentIndex(Math.max(index, 0));
}
function throttledUpdateActiveLink() {
if (timeoutRef.current === null) {
timeoutRef.current = window.setTimeout(() => {
timeoutRef.current = null;
updateActiveLink();
}, 100);
}
}
document.addEventListener('scroll', throttledUpdateActiveLink);
document.addEventListener('resize', throttledUpdateActiveLink);
updateActiveLink();
return () => {
if (timeoutRef.current != null) {
clearTimeout(timeoutRef.current);
timeoutRef.current = null;
}
document.removeEventListener('scroll', throttledUpdateActiveLink);
document.removeEventListener('resize', throttledUpdateActiveLink);
};
}, []);
return {
currentIndex,
};
}
================================================
FILE: src/components/Logo.tsx
================================================
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
/*
* Copyright (c) Facebook, Inc. and its affiliates.
*/
import type {SVGProps} from 'react';
export function Logo(props: SVGProps) {
return (
);
}
================================================
FILE: src/components/MDX/BlogCard.tsx
================================================
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
/*
* Copyright (c) Facebook, Inc. and its affiliates.
*/
import * as React from 'react';
import Link from 'next/link';
export interface BlogCardProps {
title?: string;
badge?: boolean;
icon?: string;
date?: string;
url?: string;
children?: React.ReactNode;
}
function BlogCard({title, badge, date, icon, url, children}: BlogCardProps) {
return (
);
}
================================================
FILE: src/components/MDX/Challenges/index.tsx
================================================
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
/*
* Copyright (c) Facebook, Inc. and its affiliates.
*/
import * as React from 'react';
export {Challenges} from './Challenges';
export function Hint({children}: {children: React.ReactNode}) {
return
{children}
;
}
export function Solution({children}: {children: React.ReactNode}) {
return
{children}
;
}
================================================
FILE: src/components/MDX/CodeBlock/CodeBlock.tsx
================================================
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
/*
* Copyright (c) Facebook, Inc. and its affiliates.
*/
import cn from 'classnames';
import {HighlightStyle} from '@codemirror/language';
import {highlightTree} from '@lezer/highlight';
import {javascript} from '@codemirror/lang-javascript';
import {html} from '@codemirror/lang-html';
import {css} from '@codemirror/lang-css';
import rangeParser from 'parse-numeric-range';
import {tags} from '@lezer/highlight';
import {CustomTheme} from '../Sandpack/Themes';
interface InlineHighlight {
step: number;
line: number;
startColumn: number;
endColumn: number;
}
const jsxLang = javascript({jsx: true, typescript: false});
const cssLang = css();
const htmlLang = html();
const CodeBlock = function CodeBlock({
children: {
props: {className = 'language-js', children: code = '', meta},
},
noMargin,
noShadow,
onLineHover,
}: {
children: React.ReactNode & {
props: {
className: string;
children?: string;
meta?: string;
};
};
className?: string;
noMargin?: boolean;
noShadow?: boolean;
onLineHover?: (lineNumber: number | null) => void;
}) {
code = code.trimEnd();
let lang = jsxLang;
if (className === 'language-css') {
lang = cssLang;
} else if (className === 'language-html') {
lang = htmlLang;
}
const tree = lang.language.parser.parse(code);
let tokenStarts = new Map();
let tokenEnds = new Map();
const highlightTheme = getSyntaxHighlight(CustomTheme);
highlightTree(tree, highlightTheme, (from, to, className) => {
tokenStarts.set(from, className);
tokenEnds.set(to, className);
});
const highlightedLines = new Map();
const lines = code.split('\n');
const lineDecorators = getLineDecorators(code, meta);
for (let decorator of lineDecorators) {
highlightedLines.set(decorator.line - 1, decorator.className);
}
const inlineDecorators = getInlineDecorators(code, meta);
const decoratorStarts = new Map();
const decoratorEnds = new Map();
for (let decorator of inlineDecorators) {
// Find where inline highlight starts and ends.
let decoratorStart = 0;
for (let i = 0; i < decorator.line - 1; i++) {
decoratorStart += lines[i].length + 1;
}
decoratorStart += decorator.startColumn;
const decoratorEnd =
decoratorStart + (decorator.endColumn - decorator.startColumn);
if (decoratorStarts.has(decoratorStart)) {
throw Error('Already opened decorator at ' + decoratorStart);
}
decoratorStarts.set(decoratorStart, decorator.className);
if (decoratorEnds.has(decoratorEnd)) {
throw Error('Already closed decorator at ' + decoratorEnd);
}
decoratorEnds.set(decoratorEnd, decorator.className);
}
// Produce output based on tokens and decorators.
// We assume tokens never overlap other tokens, and
// decorators never overlap with other decorators.
// However, tokens and decorators may mutually overlap.
// In that case, decorators always take precedence.
let currentDecorator = null;
let currentToken = null;
let buffer = '';
let lineIndex = 0;
let lineOutput = [];
let finalOutput = [];
for (let i = 0; i < code.length; i++) {
if (tokenEnds.has(i)) {
if (!currentToken) {
throw Error('Cannot close token at ' + i + ' because it was not open.');
}
if (!currentDecorator) {
lineOutput.push(
{buffer}
);
buffer = '';
}
currentToken = null;
}
if (decoratorEnds.has(i)) {
if (!currentDecorator) {
throw Error(
'Cannot close decorator at ' + i + ' because it was not open.'
);
}
lineOutput.push(
{buffer}
);
buffer = '';
currentDecorator = null;
}
if (decoratorStarts.has(i)) {
if (currentDecorator) {
throw Error(
'Cannot open decorator at ' + i + ' before closing last one.'
);
}
if (currentToken) {
lineOutput.push(
{buffer}
);
buffer = '';
} else {
lineOutput.push(buffer);
buffer = '';
}
currentDecorator = decoratorStarts.get(i);
}
if (tokenStarts.has(i)) {
if (currentToken) {
throw Error('Cannot open token at ' + i + ' before closing last one.');
}
currentToken = tokenStarts.get(i);
if (!currentDecorator) {
lineOutput.push(buffer);
buffer = '';
}
}
if (code[i] === '\n') {
lineOutput.push(buffer);
buffer = '';
const currentLineIndex = lineIndex;
finalOutput.push(
);
}
================================================
FILE: src/components/MDX/Diagram.tsx
================================================
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
/*
* Copyright (c) Facebook, Inc. and its affiliates.
*/
import Image from 'next/image';
interface DiagramProps {
name: string;
alt: string;
height: number;
width: number;
children: string;
captionPosition: 'top' | 'bottom' | null;
}
function Caption({text}: {text: string}) {
return (
)}
);
}
export default Diagram;
================================================
FILE: src/components/MDX/DiagramGroup.tsx
================================================
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
/*
* Copyright (c) Facebook, Inc. and its affiliates.
*/
import {ReactNode} from 'react';
interface DiagramGroupProps {
children: ReactNode;
}
export function DiagramGroup({children}: DiagramGroupProps) {
return (
{children}
);
}
export default DiagramGroup;
================================================
FILE: src/components/MDX/ErrorDecoder.tsx
================================================
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
import {useEffect, useState} from 'react';
import {useErrorDecoderParams} from '../ErrorDecoderContext';
import cn from 'classnames';
function replaceArgs(
msg: string,
argList: Array,
replacer = '[missing argument]'
): string {
let argIdx = 0;
return msg.replace(/%s/g, function () {
const arg = argList[argIdx++];
// arg can be an empty string: ?args[0]=&args[1]=count
return arg === undefined ? replacer : arg;
});
}
/**
* Sindre Sorhus
* Released under MIT license
* https://github.com/sindresorhus/linkify-urls/blob/b2397096df152e2f799011f7a48e5f73b4bf1c7e/index.js#L5C1-L7C1
*
* The regex is used to extract URL from the string for linkify.
*/
const urlRegex = () =>
/((?:https?(?::\/\/))(?:www\.)?(?:[a-zA-Z\d-_.]+(?:(?:\.|@)[a-zA-Z\d]{2,})|localhost)(?:(?:[-a-zA-Z\d:%_+.~#!?&//=@]*)(?:[,](?![\s]))*)*)/g;
// When the message contains a URL (like https://fb.me/react-refs-must-have-owner),
// make it a clickable link.
function urlify(str: string): React.ReactNode[] {
const segments = str.split(urlRegex());
return segments.map((message, i) => {
if (i % 2 === 1) {
return (
{message}
);
}
return message;
});
}
// `?args[]=foo&args[]=bar`
// or `// ?args[0]=foo&args[1]=bar`
function parseQueryString(search: string): Array {
const rawQueryString = search.substring(1);
if (!rawQueryString) {
return [];
}
const args: Array = [];
const queries = rawQueryString.split('&');
for (let i = 0; i < queries.length; i++) {
const query = decodeURIComponent(queries[i]);
if (query.startsWith('args[')) {
args.push(query.slice(query.indexOf(']=') + 2));
}
}
return args;
}
export default function ErrorDecoder() {
const {errorMessage, errorCode} = useErrorDecoderParams();
/** error messages that contain %s require reading location.search */
const hasParams = errorMessage?.includes('%s');
const [message, setMessage] = useState(() =>
errorMessage ? urlify(errorMessage) : null
);
const [isReady, setIsReady] = useState(errorMessage == null || !hasParams);
useEffect(() => {
if (errorMessage == null || !hasParams) {
return;
}
const args = parseQueryString(window.location.search);
let message = errorMessage;
if (errorCode === '418') {
// Hydration errors have a %s for the diff, but we don't add that to the args for security reasons.
message = message.replace(/%s$/, '');
// Before React 19.1, the error message didn't have an arg, and was always HTML.
if (args.length === 0) {
args.push('HTML');
} else if (args.length === 1 && args[0] === '') {
args[0] = 'HTML';
}
}
setMessage(urlify(replaceArgs(message, args, '[missing argument]')));
setIsReady(true);
}, [errorCode, hasParams, errorMessage]);
return (
{message}
);
}
================================================
FILE: src/components/MDX/ExpandableCallout.tsx
================================================
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
/*
* Copyright (c) Facebook, Inc. and its affiliates.
*/
import * as React from 'react';
import cn from 'classnames';
import {IconNote} from '../Icon/IconNote';
import {IconWarning} from '../Icon/IconWarning';
import {IconPitfall} from '../Icon/IconPitfall';
import {IconCanary} from '../Icon/IconCanary';
import {IconRocket} from '../Icon/IconRocket';
type CalloutVariants =
| 'deprecated'
| 'pitfall'
| 'note'
| 'wip'
| 'canary'
| 'experimental'
| 'rc'
| 'major'
| 'rsc';
interface ExpandableCalloutProps {
children: React.ReactNode;
type: CalloutVariants;
}
const variantMap = {
deprecated: {
title: '더 이상 사용되지 않습니다!',
Icon: IconWarning,
containerClasses: 'bg-red-5 dark:bg-red-60 dark:bg-opacity-20',
textColor: 'text-red-50 dark:text-red-40',
overlayGradient:
'linear-gradient(rgba(249, 247, 243, 0), rgba(249, 247, 243, 1)',
},
note: {
title: '중요합니다!',
Icon: IconNote,
containerClasses:
'bg-green-5 dark:bg-green-60 dark:bg-opacity-20 text-primary dark:text-primary-dark text-lg',
textColor: 'text-green-60 dark:text-green-40',
overlayGradient:
'linear-gradient(rgba(245, 249, 248, 0), rgba(245, 249, 248, 1)',
},
rc: {
title: 'RC',
Icon: IconCanary,
containerClasses:
'bg-gray-5 dark:bg-gray-60 dark:bg-opacity-20 text-primary dark:text-primary-dark text-lg',
textColor: 'text-gray-60 dark:text-gray-30',
overlayGradient:
'linear-gradient(rgba(245, 249, 248, 0), rgba(245, 249, 248, 1)',
},
canary: {
title: 'Canary',
Icon: IconCanary,
containerClasses:
'bg-gray-5 dark:bg-gray-60 dark:bg-opacity-20 text-primary dark:text-primary-dark text-lg',
textColor: 'text-gray-60 dark:text-gray-30',
overlayGradient:
'linear-gradient(rgba(245, 249, 248, 0), rgba(245, 249, 248, 1)',
},
experimental: {
title: '실험적 기능',
Icon: IconCanary,
containerClasses:
'bg-green-5 dark:bg-green-60 dark:bg-opacity-20 text-primary dark:text-primary-dark text-lg',
textColor: 'text-green-60 dark:text-green-40',
overlayGradient:
'linear-gradient(rgba(245, 249, 248, 0), rgba(245, 249, 248, 1)',
},
pitfall: {
title: '주의하세요!',
Icon: IconPitfall,
containerClasses: 'bg-yellow-5 dark:bg-yellow-60 dark:bg-opacity-20',
textColor: 'text-yellow-50 dark:text-yellow-40',
overlayGradient:
'linear-gradient(rgba(249, 247, 243, 0), rgba(249, 247, 243, 1)',
},
wip: {
title: '개발중이에요!',
Icon: IconNote,
containerClasses: 'bg-yellow-5 dark:bg-yellow-60 dark:bg-opacity-20',
textColor: 'text-yellow-50 dark:text-yellow-40',
overlayGradient:
'linear-gradient(rgba(249, 247, 243, 0), rgba(249, 247, 243, 1)',
},
major: {
title: 'React 19',
Icon: IconRocket,
containerClasses: 'bg-blue-10 dark:bg-blue-60 dark:bg-opacity-20',
textColor: 'text-blue-50 dark:text-blue-40',
overlayGradient:
'linear-gradient(rgba(249, 247, 243, 0), rgba(249, 247, 243, 1)',
},
rsc: {
title: 'React 서버 컴포넌트',
Icon: null,
containerClasses: 'bg-blue-10 dark:bg-blue-60 dark:bg-opacity-20',
textColor: 'text-blue-50 dark:text-blue-40',
overlayGradient:
'linear-gradient(rgba(249, 247, 243, 0), rgba(249, 247, 243, 1)',
},
};
function ExpandableCallout({children, type = 'note'}: ExpandableCalloutProps) {
const variant = variantMap[type];
return (
{variant.Icon && (
)}
{variant.title}
{children}
);
}
export default ExpandableCallout;
================================================
FILE: src/components/MDX/ExpandableExample.tsx
================================================
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
/*
* Copyright (c) Facebook, Inc. and its affiliates.
*/
import * as React from 'react';
import cn from 'classnames';
import {IconChevron} from '../Icon/IconChevron';
import {IconDeepDive} from '../Icon/IconDeepDive';
import {IconCodeBlock} from '../Icon/IconCodeBlock';
import {Button} from '../Button';
import {H4} from './Heading';
import {useRouter} from 'next/router';
import {useEffect, useRef, useState} from 'react';
interface ExpandableExampleProps {
children: React.ReactNode;
excerpt?: string;
type: 'DeepDive' | 'Example';
}
function ExpandableExample({children, excerpt, type}: ExpandableExampleProps) {
if (!Array.isArray(children) || children[0].type.mdxName !== 'h4') {
throw Error(
`Expandable content ${type} is missing a corresponding title at the beginning`
);
}
const isDeepDive = type === 'DeepDive';
const isExample = type === 'Example';
const id = children[0].props.id;
const {asPath} = useRouter();
const shouldAutoExpand = id === asPath.split('#')[1];
const queuedExpandRef = useRef(shouldAutoExpand);
const [isExpanded, setIsExpanded] = useState(false);
useEffect(() => {
if (queuedExpandRef.current) {
queuedExpandRef.current = false;
setIsExpanded(true);
}
}, []);
return (
{
setIsExpanded(e.currentTarget!.open);
}}
className={cn(
'my-12 rounded-2xl shadow-inner-border dark:shadow-inner-border-dark relative',
{
'dark:bg-opacity-20 dark:bg-purple-60 bg-purple-5': isDeepDive,
'dark:bg-opacity-20 dark:bg-yellow-60 bg-yellow-5': isExample,
}
)}>
{
// We toggle using a button instead of this whole area,
// with an escape case for the header anchor link
if (!(e.target instanceof SVGElement)) {
e.preventDefault();
}
}}>
{isDeepDive && (
<>
자세히 살펴보기
>
)}
{isExample && (
<>
Example
>
)}
{children[0].props.children}
{excerpt &&
{excerpt}
}
{children.slice(1)}
);
}
export default ExpandableExample;
================================================
FILE: src/components/MDX/Heading.tsx
================================================
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
/*
* Copyright (c) Facebook, Inc. and its affiliates.
*/
import cn from 'classnames';
import * as React from 'react';
import {forwardRefWithAs} from 'utils/forwardRefWithAs';
export interface HeadingProps {
className?: string;
isPageAnchor?: boolean;
children: React.ReactNode;
id?: string;
as?: any;
}
const Heading = forwardRefWithAs(function Heading(
{as: Comp = 'div', className, children, id, isPageAnchor = true, ...props},
ref
) {
let label = 'Link for this heading';
if (typeof children === 'string') {
label = 'Link for ' + children;
}
return (
{children}
{isPageAnchor && (
)}
);
});
export const H1 = ({className, ...props}: HeadingProps) => (
);
export const H2 = ({className, ...props}: HeadingProps) => (
);
export const H3 = ({className, ...props}: HeadingProps) => (
);
export const H4 = ({className, ...props}: HeadingProps) => (
);
export const H5 = ({className, ...props}: HeadingProps) => (
);
================================================
FILE: src/components/MDX/InlineCode.tsx
================================================
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
/*
* Copyright (c) Facebook, Inc. and its affiliates.
*/
import cn from 'classnames';
import type {HTMLAttributes} from 'react';
interface InlineCodeProps {
isLink?: boolean;
meta?: string;
}
function InlineCode({
isLink,
...props
}: HTMLAttributes & InlineCodeProps) {
return (
in case of RTL languages to avoid like `()console.log` to be rendered as `console.log()`
className={cn(
'inline text-code text-secondary dark:text-secondary-dark px-1 rounded-md no-underline',
{
'bg-gray-30 bg-opacity-10 py-px': !isLink,
'bg-highlight dark:bg-highlight-dark py-0': isLink,
}
)}
{...props}
/>
);
}
export default InlineCode;
================================================
FILE: src/components/MDX/Intro.tsx
================================================
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
/*
* Copyright (c) Facebook, Inc. and its affiliates.
*/
import * as React from 'react';
export interface IntroProps {
children?: React.ReactNode;
}
function Intro({children}: IntroProps) {
return (
{children}
);
}
export default Intro;
================================================
FILE: src/components/MDX/LanguagesContext.tsx
================================================
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
/*
* Copyright (c) Facebook, Inc. and its affiliates.
*/
import {createContext} from 'react';
export type LanguageItem = {
code: string;
name: string;
enName: string;
};
export type Languages = Array;
export const LanguagesContext = createContext(null);
================================================
FILE: src/components/MDX/Link.tsx
================================================
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
/*
* Copyright (c) Facebook, Inc. and its affiliates.
*/
import {Children, cloneElement} from 'react';
import NextLink from 'next/link';
import cn from 'classnames';
import {ExternalLink} from 'components/ExternalLink';
function Link({
href,
className,
children,
...props
}: React.AnchorHTMLAttributes) {
const classes =
'inline text-link dark:text-link-dark border-b border-link border-opacity-0 hover:border-opacity-100 duration-100 ease-in transition leading-normal';
const modifiedChildren = Children.toArray(children).map((child: any) => {
if (child.type?.mdxName && child.type?.mdxName === 'inlineCode') {
return cloneElement(child, {
isLink: true,
});
}
return child;
});
if (!href) {
// eslint-disable-next-line jsx-a11y/anchor-has-content
return ;
}
return (
<>
{href.startsWith('https://') ? (
{modifiedChildren}
) : href.startsWith('#') ? (
// eslint-disable-next-line jsx-a11y/anchor-has-content
{modifiedChildren}
) : (
{modifiedChildren}
)}
>
);
}
export default Link;
================================================
FILE: src/components/MDX/MDXComponents.module.css
================================================
/*
* Copyright (c) Facebook, Inc. and its affiliates.
*/
/* Stop purging. */
.markdown p {
@apply mb-4 leading-7 whitespace-pre-wrap text-gray-70;
}
.markdown ol {
@apply mb-4 ms-8 list-decimal;
}
.markdown ul {
@apply mb-4 ms-8 list-disc;
}
.markdown h1 {
@apply mb-6 text-4xl font-extrabold tracking-tight;
}
.markdown h2 {
@apply mt-12 mb-4 text-3xl font-extrabold tracking-tight;
}
.markdown h3 {
@apply mt-8 mb-3 text-2xl font-extrabold tracking-tight;
}
.markdown h4 {
@apply mt-8 mb-3 text-xl font-extrabold tracking-tight;
}
.markdown code {
@apply text-gray-70 bg-card dark:bg-card-dark p-1 rounded-lg no-underline;
font-size: 90%;
}
.markdown li {
@apply mb-2;
}
.markdown a {
@apply inline text-link dark:text-link-dark break-normal border-b border-link border-opacity-0 hover:border-opacity-100 duration-100 ease-in transition leading-normal;
}
================================================
FILE: src/components/MDX/MDXComponents.tsx
================================================
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
/*
* Copyright (c) Facebook, Inc. and its affiliates.
*/
import {Children, useContext, useMemo} from 'react';
import * as React from 'react';
import cn from 'classnames';
import type {HTMLAttributes} from 'react';
import CodeBlock from './CodeBlock';
import {CodeDiagram} from './CodeDiagram';
import {ConsoleBlock, ConsoleLogLine, ConsoleBlockMulti} from './ConsoleBlock';
import ExpandableCallout from './ExpandableCallout';
import ExpandableExample from './ExpandableExample';
import {H1, H2, H3, H4, H5} from './Heading';
import InlineCode from './InlineCode';
import Intro from './Intro';
import BlogCard from './BlogCard';
import Link from './Link';
import {PackageImport} from './PackageImport';
import Recap from './Recap';
import {SandpackClient as Sandpack, SandpackRSC} from './Sandpack';
import SandpackWithHTMLOutput from './SandpackWithHTMLOutput';
import Diagram from './Diagram';
import DiagramGroup from './DiagramGroup';
import SimpleCallout from './SimpleCallout';
import TerminalBlock from './TerminalBlock';
import YouWillLearnCard from './YouWillLearnCard';
import {Challenges, Hint, Solution} from './Challenges';
import {IconNavArrow} from '../Icon/IconNavArrow';
import ButtonLink from 'components/ButtonLink';
import {TocContext} from './TocContext';
import type {Toc, TocItem} from './TocContext';
import {TeamMember} from './TeamMember';
import {LanguagesContext} from './LanguagesContext';
import {finishedTranslations} from 'utils/finishedTranslations';
import ErrorDecoder from './ErrorDecoder';
import {IconCanary} from '../Icon/IconCanary';
import {IconExperimental} from 'components/Icon/IconExperimental';
function CodeStep({children, step}: {children: any; step: number}) {
return (
{children}
);
}
const P = (p: HTMLAttributes) => (
);
const Strong = (strong: HTMLAttributes) => (
);
const OL = (p: HTMLAttributes) => (
);
const LI = (p: HTMLAttributes) => (
);
const UL = (p: HTMLAttributes) => (
;
},
Pitfall,
Deprecated,
Wip,
Illustration,
IllustrationBlock,
Intro,
InlineToc,
LanguageList,
LearnMore,
Math,
MathI,
Note,
RC,
Canary,
Experimental,
ExperimentalBadge,
CanaryBadge,
NextMajor,
NextMajorBadge,
RSC,
RSCBadge,
PackageImport,
ReadBlogPost,
Recap,
Recipes,
Sandpack,
SandpackRSC,
SandpackWithHTMLOutput,
TeamMember,
TerminalBlock,
YouWillLearn,
YouWillLearnCard,
Challenges,
Hint,
Solution,
CodeStep,
YouTubeIframe,
ErrorDecoder,
};
for (let key in MDXComponents) {
if (MDXComponents.hasOwnProperty(key)) {
const MDXComponent: any = (MDXComponents as any)[key];
MDXComponent.mdxName = key;
}
}
================================================
FILE: src/components/MDX/PackageImport.tsx
================================================
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
/*
* Copyright (c) Facebook, Inc. and its affiliates.
*/
import {Children} from 'react';
import * as React from 'react';
import CodeBlock from './CodeBlock';
interface PackageImportProps {
children: React.ReactNode;
}
export function PackageImport({children}: PackageImportProps) {
const terminal = Children.toArray(children).filter((child: any) => {
return child.type?.mdxName !== 'pre';
});
const code = Children.toArray(children).map((child: any, i: number) => {
if (child.type?.mdxName === 'pre') {
return (
);
} else {
return null;
}
});
return (
{terminal}
{code}
);
}
================================================
FILE: src/components/MDX/Recap.tsx
================================================
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
/*
* Copyright (c) Facebook, Inc. and its affiliates.
*/
import * as React from 'react';
import {H2} from './Heading';
interface RecapProps {
children: React.ReactNode;
}
function Recap({children}: RecapProps) {
return (
요약
{children}
);
}
export default Recap;
================================================
FILE: src/components/MDX/Sandpack/ClearButton.tsx
================================================
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
/*
* Copyright (c) Facebook, Inc. and its affiliates.
*/
import * as React from 'react';
import {IconClose} from '../../Icon/IconClose';
export interface ClearButtonProps {
onClear: () => void;
}
export function ClearButton({onClear}: ClearButtonProps) {
return (
);
}
================================================
FILE: src/components/MDX/Sandpack/Console.tsx
================================================
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
/*
* Copyright (c) Facebook, Inc. and its affiliates.
*/
import cn from 'classnames';
import {useState, useRef, useEffect} from 'react';
import {IconChevron} from 'components/Icon/IconChevron';
import {
SandpackCodeViewer,
useSandpack,
} from '@codesandbox/sandpack-react/unstyled';
import type {SandpackMessageConsoleMethods} from '@codesandbox/sandpack-client';
const getType = (
message: SandpackMessageConsoleMethods
): 'info' | 'warning' | 'error' => {
if (message === 'log' || message === 'info') {
return 'info';
}
if (message === 'warn') {
return 'warning';
}
return 'error';
};
const getColor = (message: SandpackMessageConsoleMethods): string => {
if (message === 'warn') {
return 'text-yellow-50';
} else if (message === 'error') {
return 'text-red-40';
} else {
return 'text-secondary dark:text-secondary-dark';
}
};
// based on https://github.com/tmpfs/format-util/blob/0e62d430efb0a1c51448709abd3e2406c14d8401/format.js#L1
// based on https://developer.mozilla.org/en-US/docs/Web/API/console#Using_string_substitutions
// Implements s, d, i and f placeholders
function formatStr(...inputArgs: any[]): any[] {
const maybeMessage = inputArgs[0];
if (typeof maybeMessage !== 'string') {
return inputArgs;
}
// If the first argument is a string, check for substitutions.
const args = inputArgs.slice(1);
let formatted: string = String(maybeMessage);
if (args.length) {
const REGEXP = /(%?)(%([jds]))/g;
formatted = formatted.replace(REGEXP, (match, escaped, ptn, flag) => {
let arg = args.shift();
switch (flag) {
case 's':
arg += '';
break;
case 'd':
case 'i':
arg = parseInt(arg, 10).toString();
break;
case 'f':
arg = parseFloat(arg).toString();
break;
}
if (!escaped) {
return arg;
}
args.unshift(arg);
return match;
});
}
// Arguments that remain after formatting.
if (args.length) {
for (let i = 0; i < args.length; i++) {
formatted += ' ' + String(args[i]);
}
}
// Update escaped %% values.
return [formatted.replace(/%{2,2}/g, '%')];
}
type ConsoleData = Array<{
data: Array>;
id: string;
method: SandpackMessageConsoleMethods;
}>;
const MAX_MESSAGE_COUNT = 100;
export const SandpackConsole = ({visible}: {visible: boolean}) => {
const {listen} = useSandpack();
const [logs, setLogs] = useState([]);
const wrapperRef = useRef(null);
useEffect(() => {
let isActive = true;
const unsubscribe = listen((message) => {
if (!isActive) {
console.warn('Received an unexpected log from Sandpack.');
return;
}
if (
(message.type === 'start' && message.firstLoad) ||
message.type === 'refresh'
) {
setLogs([]);
}
if (message.type === 'console' && message.codesandbox) {
setLogs((prev) => {
const newLogs = message.log
.filter((consoleData) => {
if (!consoleData.method || !consoleData.data) {
return false;
}
if (
typeof consoleData.data[0] === 'string' &&
consoleData.data[0].indexOf('The above error occurred') !== -1
) {
// Don't show React error addendum because
// we have a custom error overlay.
return false;
}
return true;
})
.map((consoleData) => {
return {
...consoleData,
data: formatStr(...consoleData.data),
};
});
let messages = [...prev, ...newLogs];
while (messages.length > MAX_MESSAGE_COUNT) {
messages.shift();
}
return messages;
});
}
});
return () => {
unsubscribe();
isActive = false;
};
}, [listen]);
const [isExpanded, setIsExpanded] = useState(true);
useEffect(() => {
if (wrapperRef.current) {
wrapperRef.current.scrollTop = wrapperRef.current.scrollHeight;
}
}, [logs]);
if (!visible || logs.length === 0) {
return null;
}
return (
);
};
================================================
FILE: src/components/MDX/Sandpack/CustomPreset.tsx
================================================
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
/*
* Copyright (c) Facebook, Inc. and its affiliates.
*/
import {memo, useRef, useState} from 'react';
import {flushSync} from 'react-dom';
import {
useSandpack,
useActiveCode,
SandpackCodeEditor,
SandpackLayout,
} from '@codesandbox/sandpack-react/unstyled';
import cn from 'classnames';
import {IconChevron} from 'components/Icon/IconChevron';
import {NavigationBar} from './NavigationBar';
import {Preview} from './Preview';
import {useSandpackLint} from './useSandpackLint';
export const CustomPreset = memo(function CustomPreset({
providedFiles,
showOpenInCodeSandbox = true,
}: {
providedFiles: Array;
showOpenInCodeSandbox?: boolean;
}) {
const {lintErrors, lintExtensions} = useSandpackLint();
const {sandpack} = useSandpack();
const {code} = useActiveCode();
const {activeFile} = sandpack;
const lineCountRef = useRef<{[key: string]: number}>({});
if (!lineCountRef.current[activeFile]) {
// eslint-disable-next-line react-compiler/react-compiler
lineCountRef.current[activeFile] = code.split('\n').length;
}
const lineCount = lineCountRef.current[activeFile];
const isExpandable = lineCount > 16;
return (
);
});
const SandboxShell = memo(function SandboxShell({
providedFiles,
lintErrors,
lintExtensions,
isExpandable,
showOpenInCodeSandbox,
}: {
providedFiles: Array;
lintErrors: Array;
lintExtensions: Array;
isExpandable: boolean;
showOpenInCodeSandbox: boolean;
}) {
const containerRef = useRef(null);
const [isExpanded, setIsExpanded] = useState(false);
return (
<>
{(isExpandable || isExpanded) && (
)}
>
);
});
const Editor = memo(function Editor({
lintExtensions,
}: {
lintExtensions: Array;
}) {
return (
);
});
================================================
FILE: src/components/MDX/Sandpack/DownloadButton.tsx
================================================
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
/*
* Copyright (c) Facebook, Inc. and its affiliates.
*/
import {useSyncExternalStore} from 'react';
import {useSandpack} from '@codesandbox/sandpack-react/unstyled';
import {IconDownload} from '../../Icon/IconDownload';
import {AppJSPath, StylesCSSPath, SUPPORTED_FILES} from './createFileMap';
export interface DownloadButtonProps {}
let supportsImportMap = false;
function subscribe(cb: () => void) {
// This shouldn't actually need to update, but this works around
// https://github.com/facebook/react/issues/26095
let timeout = setTimeout(() => {
supportsImportMap =
(HTMLScriptElement as any).supports &&
(HTMLScriptElement as any).supports('importmap');
cb();
}, 0);
return () => clearTimeout(timeout);
}
function useSupportsImportMap() {
function getCurrentValue() {
return supportsImportMap;
}
function getServerSnapshot() {
return false;
}
return useSyncExternalStore(subscribe, getCurrentValue, getServerSnapshot);
}
export function DownloadButton({
providedFiles,
}: {
providedFiles: Array;
}) {
const {sandpack} = useSandpack();
const supported = useSupportsImportMap();
if (!supported) {
return null;
}
if (providedFiles.some((file) => !SUPPORTED_FILES.includes(file))) {
return null;
}
const downloadHTML = () => {
const css = sandpack.files[StylesCSSPath]?.code ?? '';
const code = sandpack.files[AppJSPath]?.code ?? '';
const blob = new Blob([
`
`,
]);
const url = window.URL.createObjectURL(blob);
const a = document.createElement('a');
a.style.display = 'none';
a.href = url;
a.download = 'sandbox.html';
document.body.appendChild(a);
a.click();
window.URL.revokeObjectURL(url);
};
return (
);
}
================================================
FILE: src/components/MDX/Sandpack/ErrorMessage.tsx
================================================
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
/*
* Copyright (c) Facebook, Inc. and its affiliates.
*/
interface ErrorType {
title?: string;
message: string;
column?: number;
line?: number;
path?: string;
}
export function ErrorMessage({error, ...props}: {error: ErrorType}) {
const {message, title} = error;
return (
{title || 'Error'}
{message}
);
}
================================================
FILE: src/components/MDX/Sandpack/LoadingOverlay.tsx
================================================
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
import {useState} from 'react';
import {
LoadingOverlayState,
OpenInCodeSandboxButton,
useSandpack,
} from '@codesandbox/sandpack-react/unstyled';
import {useEffect} from 'react';
const FADE_ANIMATION_DURATION = 200;
export const LoadingOverlay = ({
clientId,
dependenciesLoading,
forceLoading,
}: {
clientId: string;
dependenciesLoading: boolean;
forceLoading: boolean;
} & React.HTMLAttributes): React.ReactNode | null => {
const loadingOverlayState = useLoadingOverlayState(
clientId,
dependenciesLoading,
forceLoading
);
if (loadingOverlayState === 'HIDDEN') {
return null;
}
if (loadingOverlayState === 'TIMEOUT') {
return (
Unable to establish connection with the sandpack bundler. Make sure
you are online or try again later. If the problem persists, please
report it via{' '}
email
{' '}
or submit an issue on{' '}
GitHub.
{/* @ts-ignore: the OpenInCodeSandboxButton type from '@codesandbox/sandpack-react/unstyled' is incompatible with JSX in React 19 */}
);
};
const useLoadingOverlayState = (
clientId: string,
dependenciesLoading: boolean,
forceLoading: boolean
): LoadingOverlayState => {
const {sandpack, listen} = useSandpack();
const [state, setState] = useState('HIDDEN');
if (state !== 'LOADING' && forceLoading) {
setState('LOADING');
}
/**
* Sandpack listener
*/
const sandpackIdle = sandpack.status === 'idle';
useEffect(() => {
const unsubscribe = listen((message) => {
if (message.type === 'done') {
setState((prev) => {
return prev === 'LOADING' ? 'PRE_FADING' : 'HIDDEN';
});
}
}, clientId);
return () => {
unsubscribe();
};
}, [listen, clientId, sandpackIdle]);
/**
* Fading transient state
*/
useEffect(() => {
let fadeTimeout: ReturnType;
if (state === 'PRE_FADING' && !dependenciesLoading) {
setState('FADING');
} else if (state === 'FADING') {
fadeTimeout = setTimeout(
() => setState('HIDDEN'),
FADE_ANIMATION_DURATION
);
}
return () => {
clearTimeout(fadeTimeout);
};
}, [state, dependenciesLoading]);
if (sandpack.status === 'timeout') {
return 'TIMEOUT';
}
if (sandpack.status !== 'running') {
return 'HIDDEN';
}
return state;
};
================================================
FILE: src/components/MDX/Sandpack/NavigationBar.tsx
================================================
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
/*
* Copyright (c) Facebook, Inc. and its affiliates.
*/
import {
useRef,
useInsertionEffect,
useCallback,
useState,
useEffect,
Fragment,
} from 'react';
import cn from 'classnames';
import {
FileTabs,
useSandpack,
useSandpackNavigation,
} from '@codesandbox/sandpack-react/unstyled';
import {OpenInCodeSandboxButton} from './OpenInCodeSandboxButton';
import {ReloadButton} from './ReloadButton';
import {ClearButton} from './ClearButton';
import {DownloadButton} from './DownloadButton';
import {IconChevron} from '../../Icon/IconChevron';
import {Listbox} from '@headlessui/react';
import {OpenInTypeScriptPlaygroundButton} from './OpenInTypeScriptPlayground';
export function useEvent(fn: any): any {
const ref = useRef(null);
useInsertionEffect(() => {
ref.current = fn;
}, [fn]);
return useCallback((...args: any) => {
const f = ref.current!;
// @ts-ignore
return f(...args);
}, []);
}
const getFileName = (filePath: string): string => {
const lastIndexOfSlash = filePath.lastIndexOf('/');
return filePath.slice(lastIndexOfSlash + 1);
};
export function NavigationBar({
providedFiles,
showOpenInCodeSandbox = true,
}: {
providedFiles: Array;
showOpenInCodeSandbox?: boolean;
}) {
const {sandpack} = useSandpack();
const containerRef = useRef(null);
const tabsRef = useRef(null);
// By default, show the dropdown because all tabs may not fit.
// We don't know whether they'll fit or not until after hydration:
const [showDropdown, setShowDropdown] = useState(true);
const {activeFile, setActiveFile, visibleFiles, clients} = sandpack;
const clientId = Object.keys(clients)[0];
const {refresh} = useSandpackNavigation(clientId);
const isMultiFile = visibleFiles.length > 1;
const hasJustToggledDropdown = useRef(false);
// Keep track of whether we can show all tabs or just the dropdown.
const onContainerResize = useEvent((containerWidth: number) => {
if (hasJustToggledDropdown.current === true) {
// Ignore changes likely caused by ourselves.
hasJustToggledDropdown.current = false;
return;
}
if (tabsRef.current === null) {
// Some ResizeObserver calls come after unmount.
return;
}
const tabsWidth = tabsRef.current.getBoundingClientRect().width;
const needsDropdown = tabsWidth >= containerWidth;
if (needsDropdown !== showDropdown) {
hasJustToggledDropdown.current = true;
setShowDropdown(needsDropdown);
}
});
useEffect(() => {
if (isMultiFile) {
const resizeObserver = new ResizeObserver((entries) => {
for (const entry of entries) {
if (entry.contentBoxSize) {
const contentBoxSize = Array.isArray(entry.contentBoxSize)
? entry.contentBoxSize[0]
: entry.contentBoxSize;
const width = contentBoxSize.inlineSize;
onContainerResize(width);
}
}
});
const container = containerRef.current!;
resizeObserver.observe(container);
return () => resizeObserver.unobserve(container);
} else {
return;
}
// Note: in a real useEvent, onContainerResize would be omitted.
}, [isMultiFile, onContainerResize]);
const handleClear = () => {
/**
* resetAllFiles must come first, otherwise
* the previous content will appear for a second
* when the iframe loads.
*
* Plus, it should only prompt if there's any file changes
*/
if (
sandpack.editorState === 'dirty' &&
confirm('모든 수정 사항이 초기화됩니다. 계속하시겠습니까?')
) {
sandpack.resetAllFiles();
}
refresh();
};
const handleReload = () => {
refresh();
};
return (
{/* If Prettier reformats this block, the two @ts-ignore directives will no longer be adjacent to the problematic lines, causing TypeScript errors */}
{/* prettier-ignore */}
{/* @ts-ignore: the Listbox type from '@headlessui/react' is incompatible with JSX in React 19 */}
{/* @ts-ignore: the FileTabs type from '@codesandbox/sandpack-react/unstyled' is incompatible with JSX in React 19 */}