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

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions .claude/skills/docs-writer-blog/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,7 @@ See [How to Upgrade to React X.Y](/blog/YYYY/MM/DD/react-xy-upgrade-guide) for s

### React {/*react*/}

* Add `useNewHook` for [purpose]. ([#12345](https://github.com/facebook/react/pull/12345) by [@contributor](https://github.com/contributor))
* Add `useNewHook` for [purpose]. ([#12345](https://github.com/react/react/pull/12345) by [@contributor](https://github.com/contributor))

---

Expand Down Expand Up @@ -603,7 +603,7 @@ npm install react@latest react-dom@latest

| Type | Pattern |
|------|---------|
| GitHub PR | `[#12345](https://github.com/facebook/react/pull/12345)` |
| GitHub PR | `[#12345](https://github.com/react/react/pull/12345)` |
| GitHub user | `[@username](https://github.com/username)` |
| Twitter/X | `[@username](https://x.com/username)` |
| Bluesky | `[Name](https://bsky.app/profile/handle)` |
Expand All @@ -623,8 +623,8 @@ For more information, see the docs for [`useActionState`](/reference/react/useAc
### 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))
* Add `useTransition` for concurrent rendering. ([#10426](https://github.com/react/react/pull/10426) by [@acdlite](https://github.com/acdlite))
* Fix `useReducer` observing incorrect props. ([#22445](https://github.com/react/react/pull/22445) by [@josephsavona](https://github.com/josephsavona))
```

**Structure:** `Verb` + backticked API + description + `([#PR](url) by [@user](url))`
Expand Down
16 changes: 8 additions & 8 deletions .claude/skills/react-expert/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ This skill produces exhaustive documentation research on any React API or concep
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)
5. **GitHub Issues** - Confusion/questions (react/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
Expand All @@ -51,7 +51,7 @@ First, ensure the React repo is available locally:
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
git clone --depth=100 https://github.com/react/react.git .claude/react
fi
```

Expand All @@ -71,8 +71,8 @@ Spawn these agents IN PARALLEL using the Task tool. Each agent receives the skep
| 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="<topic>" --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 "<topic>" --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. |
| pr-researcher | Explore | PRs introducing/modifying API | Run `gh pr list -R react/react --search "<topic>" --state all --limit 20`. Read key PR descriptions and comments. |
| issue-hunter | Explore | Issues showing confusion | Search issues in both `react/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
Expand Down Expand Up @@ -164,8 +164,8 @@ CRITICAL: Do NOT rely on your prior knowledge. Only report what you find in PRs.

Your task: Find PRs that introduced or modified <TOPIC>.

1. Run: gh pr list -R facebook/react --search "<topic>" --state all --limit 20 --json number,title,url
2. For promising PRs, read details: gh pr view <number> -R facebook/react
1. Run: gh pr list -R react/react --search "<topic>" --state all --limit 20 --json number,title,url
2. For promising PRs, read details: gh pr view <number> -R react/react
3. Look for:
- The original RFC/motivation
- Design discussions in comments
Expand All @@ -189,7 +189,7 @@ CRITICAL: Do NOT rely on your prior knowledge. Only report what you find in issu

Your task: Find issues that reveal common confusion about <TOPIC>.

1. Search facebook/react: gh issue list -R facebook/react --search "<topic>" --state all --limit 20 --json number,title,url
1. Search react/react: gh issue list -R react/react --search "<topic>" --state all --limit 20 --json number,title,url
2. Search reactjs/react.dev: gh issue list -R reactjs/react.dev --search "<topic>" --state all --limit 20 --json number,title,url
3. For each issue, identify:
- What the user was confused about
Expand All @@ -199,7 +199,7 @@ Your task: Find issues that reveal common confusion about <TOPIC>.
Format your output as:
## Common Confusion
### Issue #<number>: <title>
**Repo:** <facebook/react or reactjs/react.dev>
**Repo:** <react/react or reactjs/react.dev>
**Confusion:** <what they misunderstood>
**Resolution:** <correct understanding>
**Gotcha:** <if applicable>
Expand Down
2 changes: 1 addition & 1 deletion .github/ISSUE_TEMPLATE/config.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
contact_links:
- name: 📃 Bugs in React
url: https://github.com/facebook/react/issues/new/choose
url: https://github.com/react/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
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/discord_notify.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ permissions: {}

jobs:
check_maintainer:
uses: facebook/react/.github/workflows/shared_check_maintainer.yml@main
uses: react/react/.github/workflows/shared_check_maintainer.yml@main
permissions:
# Used by check_maintainer
contents: read
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/label_core_team_prs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ env:

jobs:
check_maintainer:
uses: facebook/react/.github/workflows/shared_check_maintainer.yml@main
uses: react/react/.github/workflows/shared_check_maintainer.yml@main
permissions:
# Used by check_maintainer
contents: read
Expand Down
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -46,3 +46,6 @@ public/rss.xml

# worktrees
.worktrees/

# Generated OG images (scripts/generateOgImages.mjs)
public/images/og/
4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
"analyze": "ANALYZE=true next build",
"dev": "next-remote-watch ./src/content",
"prebuild:rsc": "node scripts/buildRscWorker.mjs",
"build": "node scripts/buildRscWorker.mjs && next build && node --experimental-modules ./scripts/downloadFonts.mjs",
"build": "node scripts/buildRscWorker.mjs && next build && node --experimental-modules ./scripts/downloadFonts.mjs && node ./scripts/generateOgImages.mjs",
"lint": "next lint && eslint \"src/content/**/*.md\"",
"lint:fix": "next lint --fix && eslint \"src/content/**/*.md\" --fix",
"format:source": "prettier --config .prettierrc --write \"{plugins,src}/**/*.{js,ts,jsx,tsx,css}\"",
Expand Down Expand Up @@ -51,6 +51,7 @@
"@babel/plugin-transform-modules-commonjs": "^7.18.6",
"@babel/preset-react": "^7.18.6",
"@mdx-js/mdx": "^2.1.3",
"@resvg/resvg-js": "^2.6.2",
"@types/body-scroll-lock": "^2.6.1",
"@types/classnames": "^2.2.10",
"@types/debounce": "^1.2.1",
Expand Down Expand Up @@ -102,6 +103,7 @@
"retext": "^7.0.1",
"retext-smartypants": "^4.0.0",
"rss": "^1.2.2",
"satori": "^0.26.0",
"tailwindcss": "^3.4.1",
"typescript": "^5.7.2",
"unist-util-visit": "^2.0.3",
Expand Down
Binary file added public/fonts/Optimistic_Display_W_Bd.ttf
Binary file not shown.
Binary file added public/fonts/Optimistic_Display_W_Md.ttf
Binary file not shown.
Binary file added public/images/team/gh-aurorascharff.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/images/team/gh-brentvatne.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/images/team/gh-captbaritone.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/images/team/gh-cipolleschi.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/images/team/gh-cortinico.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/images/team/gh-fbmal7.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/images/team/gh-fkgozali.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/images/team/gh-huntie.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/images/team/gh-javache.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/images/team/gh-khalef1.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/images/team/gh-kmagiera.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/images/team/gh-motiz88.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/images/team/gh-rh389.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/images/team/gh-rubennorte.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/images/team/gh-samselikoff.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/images/team/gh-simek.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/images/team/gh-stmoy.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/images/team/gh-thymikee.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/images/team/gh-vzaidman.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/images/team/jimmy-lai.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion scripts/deadLinkChecker.js
Original file line number Diff line number Diff line change
Expand Up @@ -311,7 +311,7 @@ async function buildContributorMap() {
async function fetchErrorCodes() {
try {
const response = await fetch(
'https://raw.githubusercontent.com/facebook/react/main/scripts/error-codes/codes.json'
'https://raw.githubusercontent.com/react/react/main/scripts/error-codes/codes.json'
);
if (!response.ok) {
throw new Error(`Failed to fetch error codes: ${response.status}`);
Expand Down
219 changes: 219 additions & 0 deletions scripts/generateOgImages.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,219 @@
/**
* 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.
*/

// Generates a static OG image for every content page, so social
// cards show the page title without any runtime image generation.
// Run after `next build` (see the `build` script in package.json).

import fs from 'fs';
import path from 'path';
import satori from 'satori';
import {Resvg} from '@resvg/resvg-js';
import matter from 'gray-matter';

const ROOT = process.cwd();
const CONTENT_DIR = path.join(ROOT, 'src', 'content');
const OUT_DIR = path.join(ROOT, 'public', 'images', 'og');
const CONCURRENCY = 8;

const SECTION_LABELS = {
learn: 'Learn React',
reference: 'API Reference',
community: 'Community',
blog: 'Blog',
};

const bold = fs.readFileSync(
path.join(ROOT, 'public', 'fonts', 'Optimistic_Display_W_Bd.ttf')
);
const medium = fs.readFileSync(
path.join(ROOT, 'public', 'fonts', 'Optimistic_Display_W_Md.ttf')
);

function el(type, style, children) {
return {type, props: {style, children}};
}

function card(title, pagePath) {
const section = pagePath.split('/')[1] ?? '';
const label = SECTION_LABELS[section] ?? 'React';
const logo = {
type: 'svg',
props: {
width: 80,
height: 72,
viewBox: '-10.5 -9.45 21 18.9',
fill: 'none',
children: [
{type: 'circle', props: {cx: 0, cy: 0, r: 2, fill: '#58c4dc'}},
{
type: 'g',
props: {
stroke: '#58c4dc',
strokeWidth: 1,
fill: 'none',
children: [
{type: 'ellipse', props: {rx: 10, ry: 4.5}},
{
type: 'ellipse',
props: {rx: 10, ry: 4.5, transform: 'rotate(60)'},
},
{
type: 'ellipse',
props: {rx: 10, ry: 4.5, transform: 'rotate(120)'},
},
],
},
},
],
},
};
return el(
'div',
{
width: '100%',
height: '100%',
display: 'flex',
flexDirection: 'column',
padding: '72px 80px',
backgroundColor: '#23272f',
backgroundImage:
'radial-gradient(circle at 25% 30%, #343a46 0%, #23272f 55%)',
},
[
el('div', {display: 'flex', alignItems: 'center', gap: '20px'}, [
logo,
el(
'div',
{
fontSize: 48,
fontFamily: 'Optimistic Display Bold',
color: '#f6f7f9',
},
'React'
),
]),
el(
'div',
{
flexGrow: 1,
display: 'flex',
alignItems: 'center',
fontSize: title.length > 24 ? 72 : 96,
fontFamily: 'Optimistic Display Bold',
color: '#f6f7f9',
lineHeight: 1.1,
wordBreak: 'break-word',
},
title
),
el(
'div',
{
fontSize: 40,
fontFamily: 'Optimistic Display Medium',
color: '#99a1b3',
},
label
),
]
);
}

async function renderCard(title, pagePath) {
const svg = await satori(card(title, pagePath), {
width: 1200,
height: 630,
fonts: [
{
name: 'Optimistic Display Bold',
data: bold,
weight: 700,
style: 'normal',
},
{
name: 'Optimistic Display Medium',
data: medium,
weight: 500,
style: 'normal',
},
],
});
return new Resvg(svg, {fitTo: {mode: 'width', value: 1200}}).render().asPng();
}

function collectSidebarTitles() {
const titles = new Map();
for (const name of fs.readdirSync(path.join(ROOT, 'src'))) {
if (!/^sidebar.*\.json$/.test(name)) continue;
const walk = (node) => {
if (node.path && node.title) {
titles.set(node.path, node.title);
}
for (const child of node.routes ?? []) walk(child);
};
walk(JSON.parse(fs.readFileSync(path.join(ROOT, 'src', name), 'utf8')));
}
return titles;
}

const sidebarTitles = collectSidebarTitles();

function collectPages(dir, out) {
for (const entry of fs.readdirSync(dir, {withFileTypes: true})) {
const full = path.join(dir, entry.name);
if (entry.isDirectory()) {
collectPages(full, out);
} else if (entry.name.endsWith('.md')) {
const rel = path.relative(CONTENT_DIR, full).replace(/\.md$/, '');
const segments = rel.split(path.sep);
if (segments[segments.length - 1] === 'index') {
segments.pop();
}
const pagePath = '/' + segments.join('/');
if (pagePath === '/' || pagePath.startsWith('/errors')) {
continue;
}
const {data} = matter(fs.readFileSync(full, 'utf8'));
const title = data.title ?? sidebarTitles.get(pagePath);
if (title) {
out.push({title: String(title), pagePath});
}
}
}
return out;
}

export function ogImageFileName(pagePath) {
return pagePath.replace(/^\//, '').replace(/\//g, '-') + '.png';
}

async function main() {
const pages = collectPages(CONTENT_DIR, []);
fs.mkdirSync(OUT_DIR, {recursive: true});
let done = 0;
const queue = [...pages];
async function worker() {
for (;;) {
const page = queue.shift();
if (!page) return;
const png = await renderCard(page.title, page.pagePath);
fs.writeFileSync(path.join(OUT_DIR, ogImageFileName(page.pagePath)), png);
done++;
if (done % 100 === 0) {
console.log(`og-images: ${done}/${pages.length}`);
}
}
}
await Promise.all(Array.from({length: CONCURRENCY}, worker));
console.log(`og-images: generated ${done} images in public/images/og`);
}

main().catch((error) => {
console.error(error);
process.exit(1);
});
4 changes: 2 additions & 2 deletions src/components/Layout/Footer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -344,7 +344,7 @@ export function Footer() {
<FooterLink href="/community" isHeader={true}>
Community
</FooterLink>
<FooterLink href="https://github.com/facebook/react/blob/main/CODE_OF_CONDUCT.md">
<FooterLink href="https://github.com/react/react/blob/main/CODE_OF_CONDUCT.md">
Code of Conduct
</FooterLink>
<FooterLink href="/community/team">Meet the Team</FooterLink>
Expand Down Expand Up @@ -386,7 +386,7 @@ export function Footer() {
</ExternalLink>
<ExternalLink
aria-label="React on Github"
href="https://github.com/facebook/react"
href="https://github.com/react/react"
className={socialLinkClasses}>
<IconGitHub />
</ExternalLink>
Expand Down
Loading
Loading