diff --git a/.changeset/add-octicons-react-symbols-package.md b/.changeset/add-octicons-react-symbols-package.md
new file mode 100644
index 00000000000..54711a4237c
--- /dev/null
+++ b/.changeset/add-octicons-react-symbols-package.md
@@ -0,0 +1,5 @@
+---
+'@primer/octicons-react-symbols': minor
+---
+
+Octicons React Symbols: Add a package that provides reusable SVG symbol sprites and matching icon components.
diff --git a/.prettierignore b/.prettierignore
index bcb3ba68f18..14856869843 100644
--- a/.prettierignore
+++ b/.prettierignore
@@ -6,3 +6,4 @@ docs/.cache
docs/public
**/.next/**
packages/react/src/legacy-theme/ts/color-schemes.ts
+packages/octicons-react-symbols/src/generated
diff --git a/eslint.config.mjs b/eslint.config.mjs
index 37c4c0adeb0..efa74bd1473 100644
--- a/eslint.config.mjs
+++ b/eslint.config.mjs
@@ -51,6 +51,7 @@ const config = defineConfig([
'**/.next/**/*',
'dist/**/*',
'**/dist/**/*',
+ 'packages/octicons-react-symbols/src/generated/**/*',
'script/**/*.ts',
'**/*.module.css.d.ts',
'**/.playwright/**',
diff --git a/package-lock.json b/package-lock.json
index 87cadb77973..2402da05d96 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -85,7 +85,7 @@
"react-dom": "^18.3.1"
},
"devDependencies": {
- "@primer/react": "38.28.0",
+ "@primer/react": "38.29.0",
"@primer/styled-react": "1.1.0",
"@types/react": "^18.3.11",
"@types/react-dom": "^18.3.0",
@@ -99,7 +99,7 @@
"name": "example-nextjs",
"version": "0.0.0",
"dependencies": {
- "@primer/react": "38.28.0",
+ "@primer/react": "38.29.0",
"@primer/styled-react": "1.1.0",
"next": "^16.1.7",
"react": "^19.2.0",
@@ -142,7 +142,7 @@
"version": "0.0.0",
"dependencies": {
"@primer/octicons-react": "^19.21.0",
- "@primer/react": "38.28.0",
+ "@primer/react": "38.29.0",
"@primer/styled-react": "1.1.0",
"clsx": "^2.1.1",
"next": "^16.1.7",
@@ -578,9 +578,9 @@
}
},
"node_modules/@babel/helper-plugin-utils": {
- "version": "7.28.6",
- "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.28.6.tgz",
- "integrity": "sha512-S9gzZ/bz83GRysI7gAD4wPT/AI3uCnY+9xn+Mx/KPs2JwHJIz1W8PZkg2cqyt3RNOBM8ejcXhV6y8Og7ly/Dug==",
+ "version": "7.29.7",
+ "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.29.7.tgz",
+ "integrity": "sha512-G7sHYigPY17oO5SYWnfD/0MTBwVR781S/JI643e/JhUYgVgWE/61SoW3NH9KWUKyKq5LVh3npif99Wkt6j86Jw==",
"license": "MIT",
"engines": {
"node": ">=6.9.0"
@@ -7486,7 +7486,9 @@
"link": true
},
"node_modules/@primer/octicons": {
- "version": "19.15.5",
+ "version": "19.28.1",
+ "resolved": "https://registry.npmjs.org/@primer/octicons/-/octicons-19.28.1.tgz",
+ "integrity": "sha512-pwSilXmgNrbVF2bChkh4zZtUyb4Vr4niYhA9PhUdtjVz86A2iwA/YjjopHS0suT+I7niUZJEepEpmSC7kARKNQ==",
"license": "MIT",
"dependencies": {
"object-assign": "^4.1.1"
@@ -7504,6 +7506,10 @@
"react": ">=16.3"
}
},
+ "node_modules/@primer/octicons-react-symbols": {
+ "resolved": "packages/octicons-react-symbols",
+ "link": true
+ },
"node_modules/@primer/primitives": {
"version": "11.5.1",
"resolved": "https://registry.npmjs.org/@primer/primitives/-/primitives-11.5.1.tgz",
@@ -7796,11 +7802,14 @@
"link": true
},
"node_modules/@publint/pack": {
- "version": "0.1.2",
- "resolved": "https://registry.npmjs.org/@publint/pack/-/pack-0.1.2.tgz",
- "integrity": "sha512-S+9ANAvUmjutrshV4jZjaiG8XQyuJIZ8a4utWmN/vW1sgQ9IfBnPndwkmQYw53QmouOIytT874u65HEmu6H5jw==",
+ "version": "0.1.5",
+ "resolved": "https://registry.npmjs.org/@publint/pack/-/pack-0.1.5.tgz",
+ "integrity": "sha512-edgyN2pP07uXiP4tJs0s8KVmU8M8i60YPbbI0/WDeok1mIJHRXz+CgD8I0nelwDkoCh3EWL/G5kGfbuHjsdbvw==",
"dev": true,
"license": "MIT",
+ "dependencies": {
+ "tinyexec": "^1.2.4"
+ },
"engines": {
"node": ">=18"
},
@@ -8649,9 +8658,9 @@
"license": "MIT"
},
"node_modules/@rolldown/binding-android-arm64": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.1.1.tgz",
- "integrity": "sha512-BLf9Wak/gfwVb7NQTQW4wBgL3oAfPy7ArEkhwV543OVw/uY6B47z5xYsqPSZ9PDOorvURPinws6ThaFuNgGLgA==",
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.1.2.tgz",
+ "integrity": "sha512-2cZ+7xRS+DBcuJBJKnfzsbleumJhBqSlJVpuzHC0nTqfd3QQ7Vx2/x5YR/D7cBamKSeWplwo82Fn9lqYUDEMfA==",
"cpu": [
"arm64"
],
@@ -8666,9 +8675,9 @@
}
},
"node_modules/@rolldown/binding-darwin-arm64": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.1.1.tgz",
- "integrity": "sha512-rRZRPy/Ynb+Mxu0O6tfPldHeDgAn0sRij+IOUy6sFdUlv3hArGW/DloE3GfAxtqpOJuRNgF74Nr5gM4xBeU2jQ==",
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.1.2.tgz",
+ "integrity": "sha512-RkPMJnygxsgOYdkfqgpwY0/Fzm8d0VQe6HGU2/B00Xa9eqdLbrII+DOKAodbJAn3ZL1AJxGHkZRPYazgGY6Ljw==",
"cpu": [
"arm64"
],
@@ -8683,9 +8692,9 @@
}
},
"node_modules/@rolldown/binding-darwin-x64": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.1.1.tgz",
- "integrity": "sha512-/MtefPxhKPyWWFM8L45OWiEqRf+eSU2Qv9ZAyTaoZOoGcoPKxbbhjTJO2/U2IThv0uDZ4NWHc3/oTsR6IEOtww==",
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.1.2.tgz",
+ "integrity": "sha512-Uiczh6vFhwyfd7WNe7Q7mCA4KxAiLdz7jPE/WGizfRpIieoyFuNVMmM8HqZ9HwudTkY6/AeMQwlNJ9NJijguWw==",
"cpu": [
"x64"
],
@@ -8700,9 +8709,9 @@
}
},
"node_modules/@rolldown/binding-freebsd-x64": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.1.1.tgz",
- "integrity": "sha512-202K+cpIi1kx/Zn7AtxBi4LTXSY67Aszb2K9rNsuW7FeBeh0nqoNmYLOSZidV0p88VPBzMmTZcHAdPNo3kRYzQ==",
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.1.2.tgz",
+ "integrity": "sha512-+TpdtTRgHiJFjCVFbw311SuLk3KfytPOQQn+VlAEv+gBxYPtL7E6JS9e/tk+8CwxhIZvemJKo4rTKgfWNsKkkA==",
"cpu": [
"x64"
],
@@ -8717,9 +8726,9 @@
}
},
"node_modules/@rolldown/binding-linux-arm-gnueabihf": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.1.1.tgz",
- "integrity": "sha512-wl9NfeXNUwrXtUc063tddmZFUI6qiNs1CNOwni0OL4vC7MqVSYugra3ZgtDmtVy8e0DluJTENmzIv2BwqLzT4Q==",
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.1.2.tgz",
+ "integrity": "sha512-4lv1/tkmi7ueIVHnyreaOeUpiZP26BH9rRy6hoYfR9310A2B9nUEVRDvBx69vx64Nr3eTPPRkyciqJJs+j9Jmw==",
"cpu": [
"arm"
],
@@ -8734,9 +8743,9 @@
}
},
"node_modules/@rolldown/binding-linux-arm64-gnu": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.1.1.tgz",
- "integrity": "sha512-at2EO4o7D/PJLC4Xik16bU4CcjQE2tSv1LfqMA0TRYQYQihRm3gZeDB8xaX28A9SFedibcAk5DeMCKt4REKG0A==",
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.1.2.tgz",
+ "integrity": "sha512-gBSUVO0eaWgw1JMjK3gB8BMlX2Mk148s2lTiVT3e9vjVxbl7UDfMWWY8CfIaaqiXuM9fVTMxIpUz6CAo/B6Vlw==",
"cpu": [
"arm64"
],
@@ -8754,9 +8763,9 @@
}
},
"node_modules/@rolldown/binding-linux-arm64-musl": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.1.1.tgz",
- "integrity": "sha512-5PUjZx366h9tkJTPJF5eibxOlK3sGoeRiBJLLjjEB5/kLDuhr6qB3LkhqLz1smXNgsX+pBhnbcJBrPE30HznAA==",
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.1.2.tgz",
+ "integrity": "sha512-LjQP/iZLBu8o8PjIfk4x3At0/mT6h282pvz8Z5LAyhGbu/kDezyO7ea62rF5uoqmgnIYqbN/MqJ3Si3Aymi7xQ==",
"cpu": [
"arm64"
],
@@ -8774,9 +8783,9 @@
}
},
"node_modules/@rolldown/binding-linux-ppc64-gnu": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.1.1.tgz",
- "integrity": "sha512-1WK84XPeio3tjP1sM/TMXiC0G1i1iq1qGZ71KfNQjEFLU1kwD+Cv5T8nGySg/JUFwLbaScu6ve9DmeXlmqpkFA==",
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.1.2.tgz",
+ "integrity": "sha512-X/7bVLWelEsbyWDUSXt7zVsTniLLPIY2n1rH58qr78l9i7MNbbxBWD8gI2vRfBWf4NUXJCUuQnfZDsp32LqsfQ==",
"cpu": [
"ppc64"
],
@@ -8794,9 +8803,9 @@
}
},
"node_modules/@rolldown/binding-linux-s390x-gnu": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.1.1.tgz",
- "integrity": "sha512-1nS1X5z1uMJ369RU25hTpKCFvUwXZp12dIzlzk4S+UxCTcSVGsAE6tzkOSufv/7jnmAtK0ZlrsJxh2fGmsnVSw==",
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.1.2.tgz",
+ "integrity": "sha512-gb6dYKW/1KDorGXyy48glEBJs/sxVSC5pcVrox/pFGV4mvwSFeg2sK5L2tRkVsVlh7kueqOgg4GEcuipJcGuKg==",
"cpu": [
"s390x"
],
@@ -8814,9 +8823,9 @@
}
},
"node_modules/@rolldown/binding-linux-x64-gnu": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.1.1.tgz",
- "integrity": "sha512-NwX/wspnq4vYyMFsqbYvzums3ki/Tk8FZbMzMAovPDp3OfLeYKby/D+9osokadXuYEV3OvpeHlwnr/bG8QMixA==",
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.1.2.tgz",
+ "integrity": "sha512-JY4w85pU3iAiJVMh5nuk4/Mh9GjMsupe8MrIN53rwxAZW64GKrWeJBuN6SxQg9QTU5uB1cxyhDzW8jqRn1EABw==",
"cpu": [
"x64"
],
@@ -8834,9 +8843,9 @@
}
},
"node_modules/@rolldown/binding-linux-x64-musl": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.1.1.tgz",
- "integrity": "sha512-+n46LhDrJFQM+229y4oXtVpj1G50U/+XuHMlpnisFTEXhrg9f/YIjp/HymX+PVJjBEr7XHRs3CFLelV464pqwA==",
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.1.2.tgz",
+ "integrity": "sha512-xvpA7o5KCYLB0Rwscmuylb1/zHHSUx4g4xilm4prC5jP76pEUlzBmMbgpbh7bVDbId4NcfT96gN5i6mE6UDaiw==",
"cpu": [
"x64"
],
@@ -8854,9 +8863,9 @@
}
},
"node_modules/@rolldown/binding-openharmony-arm64": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.1.1.tgz",
- "integrity": "sha512-qGwEu47zOWYo7LdRHhCWTNhzwGtxXpdY6CERs8QEOqC0PXGGics/e3vHnyEUKt8xK6YkbZXFUCeklrpB6js8ag==",
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.1.2.tgz",
+ "integrity": "sha512-p/ts6KBLjuk49Bp21XH77poQGt02iNz7ChgHep7tudPOaLinR/De/RHdxF8w8Yj4r/bF/bqXwH6PZrB2sA+Nvw==",
"cpu": [
"arm64"
],
@@ -8871,9 +8880,9 @@
}
},
"node_modules/@rolldown/binding-wasm32-wasi": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/@rolldown/binding-wasm32-wasi/-/binding-wasm32-wasi-1.1.1.tgz",
- "integrity": "sha512-qczfgEH8u0wHGGOXtA7UMAybNKuQjjEXairyQaw4WzjiMztfbgatG1h4OKays/smhtwbWltpKCRGtVhU6h40Sg==",
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-wasm32-wasi/-/binding-wasm32-wasi-1.1.2.tgz",
+ "integrity": "sha512-VMu/wmrZ9hJzYlRhbw7jK5PODlugyKZ5mOdX78+lS8OvuFkWNQdz1pFLrI2p3P0pjXOmUZ7B48o5VnMH9QOGtg==",
"cpu": [
"wasm32"
],
@@ -8881,8 +8890,8 @@
"license": "MIT",
"optional": true,
"dependencies": {
- "@emnapi/core": "1.11.0",
- "@emnapi/runtime": "1.11.0",
+ "@emnapi/core": "1.11.1",
+ "@emnapi/runtime": "1.11.1",
"@napi-rs/wasm-runtime": "^1.1.5"
},
"engines": {
@@ -8890,9 +8899,9 @@
}
},
"node_modules/@rolldown/binding-wasm32-wasi/node_modules/@emnapi/core": {
- "version": "1.11.0",
- "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.11.0.tgz",
- "integrity": "sha512-l9Oo58x0HOP5znGzVhYW9U3e5wVuA4LAZU2AGezTmkhO1CgQRFDhDg4nneHsu/t3WniXg9QrG2nIXL/ZS8ln8Q==",
+ "version": "1.11.1",
+ "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.11.1.tgz",
+ "integrity": "sha512-RSvbQmHzdKzNsLYa/wHrbc3KN4sYLKAdPZxqiM2HATqv/SBk2/ENSHpvXGaLOMcsAyz0poEGqkmmKYG3OWiJEQ==",
"dev": true,
"license": "MIT",
"optional": true,
@@ -8902,9 +8911,9 @@
}
},
"node_modules/@rolldown/binding-wasm32-wasi/node_modules/@emnapi/runtime": {
- "version": "1.11.0",
- "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.11.0.tgz",
- "integrity": "sha512-55coeOFKHv1ywEcUXJtWU5f+Jr/W5tZDvZig8DLKSwUN1JpROQ4rk/SNOQiFWmaR/VKF4zuFyW1B8JduOSv6Pg==",
+ "version": "1.11.1",
+ "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.11.1.tgz",
+ "integrity": "sha512-vgj7R3y3Wgx24IQaGPA/R6YFXLHVMOZ0uVEyIQPaWs+rd1AzfEMXlAC22FYwO1XkKR6NPsq7mUandH8oIRdZFw==",
"dev": true,
"license": "MIT",
"optional": true,
@@ -8943,9 +8952,9 @@
}
},
"node_modules/@rolldown/binding-win32-arm64-msvc": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.1.1.tgz",
- "integrity": "sha512-4psXSh63mSbwJF+mB8/9yfUUEzBiHYcUjxa32EO9ZwKy0Ypwjcg4F10D8SvVXgd+isy2UUUjF9HJJnDu1T/4Gg==",
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.1.2.tgz",
+ "integrity": "sha512-xtUJqs8qEkuSviS0n1tsohaPuz3a1SPhZywOji4Oo+sgrJs8daEDMZ0QtqL0OS7dx8PoVpg2J/ZZycPY5I2+Zg==",
"cpu": [
"arm64"
],
@@ -8960,9 +8969,9 @@
}
},
"node_modules/@rolldown/binding-win32-x64-msvc": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.1.1.tgz",
- "integrity": "sha512-MUvC/HLXVjzkQkWiExdVTEEWf0py+GfWm8WKSZsekG3ih6a21iy0BHPF07X3JIf3ifoklZXTIaHTLPBgH1C3dw==",
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.1.2.tgz",
+ "integrity": "sha512-85YiLQqjUKgSO/Zjnf9e0XIn5Ymrh1fLDWBeAkZqpuBR/3R8TpfoHXuyblqyQrftSSgWO9qpcHN8mkyKsLraoA==",
"cpu": [
"x64"
],
@@ -10097,6 +10106,7 @@
"version": "3.13.18",
"resolved": "https://registry.npmjs.org/@tanstack/react-virtual/-/react-virtual-3.13.18.tgz",
"integrity": "sha512-dZkhyfahpvlaV0rIKnvQiVoWPyURppl6w4m9IwMDpuIjcJ1sD9YGWrt0wISvgU7ewACXx2Ct46WPgI6qAD4v6A==",
+ "dev": true,
"license": "MIT",
"dependencies": {
"@tanstack/virtual-core": "3.13.18"
@@ -10114,6 +10124,7 @@
"version": "3.13.18",
"resolved": "https://registry.npmjs.org/@tanstack/virtual-core/-/virtual-core-3.13.18.tgz",
"integrity": "sha512-Mx86Hqu1k39icq2Zusq+Ey2J6dDWTjDvEv43PJtRCoEYTLyfaPnxIQ6iy7YAOK0NV/qOEmZQ/uCufrppZxTgcg==",
+ "dev": true,
"license": "MIT",
"funding": {
"type": "github",
@@ -10518,7 +10529,9 @@
}
},
"node_modules/@types/babel__generator": {
- "version": "7.6.3",
+ "version": "7.27.0",
+ "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.27.0.tgz",
+ "integrity": "sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -23287,14 +23300,14 @@
"license": "MIT"
},
"node_modules/publint": {
- "version": "0.3.15",
- "resolved": "https://registry.npmjs.org/publint/-/publint-0.3.15.tgz",
- "integrity": "sha512-xPbRAPW+vqdiaKy5sVVY0uFAu3LaviaPO3pZ9FaRx59l9+U/RKR1OEbLhkug87cwiVKxPXyB4txsv5cad67u+A==",
+ "version": "0.3.21",
+ "resolved": "https://registry.npmjs.org/publint/-/publint-0.3.21.tgz",
+ "integrity": "sha512-OqejcnMV6E9zel2oCrUOJEiiFkGiAAni0A6ibfQNh1k9Gu5z4F+Yso8lllam7AzmV6Do0vp7u3UpZNRBwuXaHQ==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@publint/pack": "^0.1.2",
- "package-manager-detector": "^1.3.0",
+ "@publint/pack": "^0.1.4",
+ "package-manager-detector": "^1.6.0",
"picocolors": "^1.1.1",
"sade": "^1.8.1"
},
@@ -24067,13 +24080,13 @@
}
},
"node_modules/rolldown": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.1.1.tgz",
- "integrity": "sha512-IN750c0p+s3jqJIsFLRZrQazmbAB1kkQDTtQjSt/gbS2ywLhlv4R5Shazer0FZKmuo/BsO3/w2UoYnUjuOZqHg==",
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.1.2.tgz",
+ "integrity": "sha512-x0CrQQqCXWGeI8dTvFfN/Dnv3yMKT9hv5jFjlOreKAx9wqLq9wz7VvLLHyaAXC90/CpggTu9SisSbsJJTPSjNQ==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@oxc-project/types": "=0.135.0",
+ "@oxc-project/types": "=0.137.0",
"@rolldown/pluginutils": "^1.0.0"
},
"bin": {
@@ -24083,27 +24096,27 @@
"node": "^20.19.0 || >=22.12.0"
},
"optionalDependencies": {
- "@rolldown/binding-android-arm64": "1.1.1",
- "@rolldown/binding-darwin-arm64": "1.1.1",
- "@rolldown/binding-darwin-x64": "1.1.1",
- "@rolldown/binding-freebsd-x64": "1.1.1",
- "@rolldown/binding-linux-arm-gnueabihf": "1.1.1",
- "@rolldown/binding-linux-arm64-gnu": "1.1.1",
- "@rolldown/binding-linux-arm64-musl": "1.1.1",
- "@rolldown/binding-linux-ppc64-gnu": "1.1.1",
- "@rolldown/binding-linux-s390x-gnu": "1.1.1",
- "@rolldown/binding-linux-x64-gnu": "1.1.1",
- "@rolldown/binding-linux-x64-musl": "1.1.1",
- "@rolldown/binding-openharmony-arm64": "1.1.1",
- "@rolldown/binding-wasm32-wasi": "1.1.1",
- "@rolldown/binding-win32-arm64-msvc": "1.1.1",
- "@rolldown/binding-win32-x64-msvc": "1.1.1"
+ "@rolldown/binding-android-arm64": "1.1.2",
+ "@rolldown/binding-darwin-arm64": "1.1.2",
+ "@rolldown/binding-darwin-x64": "1.1.2",
+ "@rolldown/binding-freebsd-x64": "1.1.2",
+ "@rolldown/binding-linux-arm-gnueabihf": "1.1.2",
+ "@rolldown/binding-linux-arm64-gnu": "1.1.2",
+ "@rolldown/binding-linux-arm64-musl": "1.1.2",
+ "@rolldown/binding-linux-ppc64-gnu": "1.1.2",
+ "@rolldown/binding-linux-s390x-gnu": "1.1.2",
+ "@rolldown/binding-linux-x64-gnu": "1.1.2",
+ "@rolldown/binding-linux-x64-musl": "1.1.2",
+ "@rolldown/binding-openharmony-arm64": "1.1.2",
+ "@rolldown/binding-wasm32-wasi": "1.1.2",
+ "@rolldown/binding-win32-arm64-msvc": "1.1.2",
+ "@rolldown/binding-win32-x64-msvc": "1.1.2"
}
},
"node_modules/rolldown/node_modules/@oxc-project/types": {
- "version": "0.135.0",
- "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.135.0.tgz",
- "integrity": "sha512-wR+xRdFkUBMvcAjBJ2q2kcZM6d+DKu2NgoOyxZgYwZdLhmiv6+rnO8PZ/P68kMiZtIKm+pW7zyEJ4kSOs0vo+Q==",
+ "version": "0.137.0",
+ "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.137.0.tgz",
+ "integrity": "sha512-WT+Gb24i8hmvo85AIv2oEYouEXkRlKAlT9WaCa3TfLgNCN+GhrJOGZuIlMouAh38Qe4QOx26eUOVsq70qXrywA==",
"dev": true,
"license": "MIT",
"funding": {
@@ -26528,9 +26541,9 @@
"license": "MIT"
},
"node_modules/tinyexec": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.0.2.tgz",
- "integrity": "sha512-W/KYk+NFhkmsYpuHq5JykngiOCnxeVL8v8dFnqxSD8qEEdRfXk1SDM6JzNqcERbcGYj9tMrDQBYV9cjgnunFIg==",
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.2.4.tgz",
+ "integrity": "sha512-SHf/r48b7vOrjve9PxJo3MN5v5yuyjHvdUcrQffT3WXMUfnGmHDVbC4k3sHJaJTgZCwpUplIaAo5ANtMyp3YHg==",
"dev": true,
"license": "MIT",
"engines": {
@@ -28996,6 +29009,13 @@
"node": ">=0.10.0"
}
},
+ "node_modules/workerpool": {
+ "version": "9.3.4",
+ "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-9.3.4.tgz",
+ "integrity": "sha512-TmPRQYYSAnnDiEB0P/Ytip7bFGvqnSU6I2BcuSw7Hx+JSg/DsUi5ebYfc8GYaSdpuvOcEs6dXxPurOYpe9QFwg==",
+ "dev": true,
+ "license": "Apache-2.0"
+ },
"node_modules/wrap-ansi": {
"version": "7.0.0",
"dev": true,
@@ -29240,7 +29260,7 @@
},
"packages/canvas": {
"name": "@primer/react-canvas",
- "version": "0.0.1",
+ "version": "0.0.2",
"devDependencies": {
"@primer/primitives": "11.x",
"@primer/react": "^38.27.0",
@@ -29742,6 +29762,253 @@
"url": "https://github.com/sponsors/isaacs"
}
},
+ "packages/octicons-react-symbols": {
+ "name": "@primer/octicons-react-symbols",
+ "version": "0.0.0",
+ "dependencies": {
+ "@babel/runtime": "^7.28.4",
+ "react-compiler-runtime": "^1.0.0"
+ },
+ "devDependencies": {
+ "@babel/core": "^7.28.5",
+ "@babel/generator": "^7.28.5",
+ "@babel/plugin-transform-runtime": "^7.28.5",
+ "@babel/preset-react": "^7.28.5",
+ "@babel/preset-typescript": "^7.28.5",
+ "@primer/octicons": "^19.21.1",
+ "@rolldown/plugin-babel": "^0.2.3",
+ "@rollup/plugin-babel": "^7.1.0",
+ "@types/babel__core": "^7.20.5",
+ "@types/babel__generator": "^7.27.0",
+ "@vitejs/plugin-react": "^6.0.2",
+ "babel-plugin-react-compiler": "^1.0.0",
+ "change-case": "^5.4.4",
+ "publint": "^0.3.16",
+ "react": "^18.3.1",
+ "rimraf": "^6.1.3",
+ "rolldown": "^1.1.2",
+ "rollup-plugin-typescript2": "^0.37.0",
+ "typescript": "^6.0.3"
+ },
+ "peerDependencies": {
+ "@types/react": "18.x || 19.x",
+ "react": "18.x || 19.x"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ }
+ }
+ },
+ "packages/octicons-react-symbols/node_modules/@rollup/plugin-babel": {
+ "version": "7.1.0",
+ "resolved": "https://registry.npmjs.org/@rollup/plugin-babel/-/plugin-babel-7.1.0.tgz",
+ "integrity": "sha512-h9Y+xYha6p4wKO+FwdiPIkE+eIYCm8MzZPpX1iARIoFBnmKP9CnpT1p9dDf/DTFm6fyN8PmuLyRI5qZgchnitw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-module-imports": "^7.18.6",
+ "@rollup/pluginutils": "^5.0.1",
+ "workerpool": "^9.0.0"
+ },
+ "engines": {
+ "node": ">=14.0.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0",
+ "@types/babel__core": "^7.1.9",
+ "rollup": "^2.0.0||^3.0.0||^4.0.0"
+ },
+ "peerDependenciesMeta": {
+ "@types/babel__core": {
+ "optional": true
+ },
+ "rollup": {
+ "optional": true
+ }
+ }
+ },
+ "packages/octicons-react-symbols/node_modules/balanced-match": {
+ "version": "4.0.4",
+ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz",
+ "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "18 || 20 || >=22"
+ }
+ },
+ "packages/octicons-react-symbols/node_modules/brace-expansion": {
+ "version": "5.0.6",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.6.tgz",
+ "integrity": "sha512-kLpxurY4Z4r9sgMsyG0Z9uzsBlgiU/EFKhj/h91/8yHu0edo7XuixOIH3VcJ8kkxs6/jPzoI6U9Vj3WqbMQ94g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "balanced-match": "^4.0.2"
+ },
+ "engines": {
+ "node": "18 || 20 || >=22"
+ }
+ },
+ "packages/octicons-react-symbols/node_modules/fs-extra": {
+ "version": "10.1.0",
+ "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz",
+ "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "graceful-fs": "^4.2.0",
+ "jsonfile": "^6.0.1",
+ "universalify": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "packages/octicons-react-symbols/node_modules/glob": {
+ "version": "13.0.6",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-13.0.6.tgz",
+ "integrity": "sha512-Wjlyrolmm8uDpm/ogGyXZXb1Z+Ca2B8NbJwqBVg0axK9GbBeoS7yGV6vjXnYdGm6X53iehEuxxbyiKp8QmN4Vw==",
+ "dev": true,
+ "license": "BlueOak-1.0.0",
+ "dependencies": {
+ "minimatch": "^10.2.2",
+ "minipass": "^7.1.3",
+ "path-scurry": "^2.0.2"
+ },
+ "engines": {
+ "node": "18 || 20 || >=22"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "packages/octicons-react-symbols/node_modules/jsonfile": {
+ "version": "6.2.1",
+ "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.1.tgz",
+ "integrity": "sha512-zwOTdL3rFQ/lRdBnntKVOX6k5cKJwEc1HdilT71BWEu7J41gXIB2MRp+vxduPSwZJPWBxEzv4yH1wYLJGUHX4Q==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "universalify": "^2.0.0"
+ },
+ "optionalDependencies": {
+ "graceful-fs": "^4.1.6"
+ }
+ },
+ "packages/octicons-react-symbols/node_modules/lru-cache": {
+ "version": "11.5.1",
+ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.5.1.tgz",
+ "integrity": "sha512-RPimw/7aMdv2oqRrxKwvZXcPfwBrn/JZ2xYcY9Hus/6LaS3VOAKVWKWgNLCFSiOm1ESXinjsDlidVU7JlnCN2A==",
+ "dev": true,
+ "license": "BlueOak-1.0.0",
+ "engines": {
+ "node": "20 || >=22"
+ }
+ },
+ "packages/octicons-react-symbols/node_modules/minimatch": {
+ "version": "10.2.5",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz",
+ "integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==",
+ "dev": true,
+ "license": "BlueOak-1.0.0",
+ "dependencies": {
+ "brace-expansion": "^5.0.5"
+ },
+ "engines": {
+ "node": "18 || 20 || >=22"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "packages/octicons-react-symbols/node_modules/path-scurry": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.2.tgz",
+ "integrity": "sha512-3O/iVVsJAPsOnpwWIeD+d6z/7PmqApyQePUtCndjatj/9I5LylHvt5qluFaBT3I5h3r1ejfR056c+FCv+NnNXg==",
+ "dev": true,
+ "license": "BlueOak-1.0.0",
+ "dependencies": {
+ "lru-cache": "^11.0.0",
+ "minipass": "^7.1.2"
+ },
+ "engines": {
+ "node": "18 || 20 || >=22"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "packages/octicons-react-symbols/node_modules/rimraf": {
+ "version": "6.1.3",
+ "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-6.1.3.tgz",
+ "integrity": "sha512-LKg+Cr2ZF61fkcaK1UdkH2yEBBKnYjTyWzTJT6KNPcSPaiT7HSdhtMXQuN5wkTX0Xu72KQ1l8S42rlmexS2hSA==",
+ "dev": true,
+ "license": "BlueOak-1.0.0",
+ "dependencies": {
+ "glob": "^13.0.3",
+ "package-json-from-dist": "^1.0.1"
+ },
+ "bin": {
+ "rimraf": "dist/esm/bin.mjs"
+ },
+ "engines": {
+ "node": "20 || >=22"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "packages/octicons-react-symbols/node_modules/rollup-plugin-typescript2": {
+ "version": "0.37.0",
+ "resolved": "https://registry.npmjs.org/rollup-plugin-typescript2/-/rollup-plugin-typescript2-0.37.0.tgz",
+ "integrity": "sha512-S1r/4Ufi13Yg/chPlh4iSHWq2Zs/sIAodW5SKUoCQfy/DEQhkS2XRFEtv+NRq3iBO4WHHfqKtDPOC5lJTYm7OQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@rollup/pluginutils": "^4.1.2",
+ "find-cache-dir": "^3.3.2",
+ "fs-extra": "^10.0.0",
+ "semver": "^7.5.4",
+ "tslib": "^2.6.2"
+ },
+ "peerDependencies": {
+ "rollup": ">=1.26.3",
+ "typescript": ">=2.4.0"
+ }
+ },
+ "packages/octicons-react-symbols/node_modules/rollup-plugin-typescript2/node_modules/@rollup/pluginutils": {
+ "version": "4.2.1",
+ "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-4.2.1.tgz",
+ "integrity": "sha512-iKnFXr7NkdZAIHiIWE+BX5ULi/ucVFYWD6TbAV+rZctiRTY2PL6tsIKhoIOaoskiWAkgu+VsbXgUVDNLHf+InQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "estree-walker": "^2.0.1",
+ "picomatch": "^2.2.2"
+ },
+ "engines": {
+ "node": ">= 8.0.0"
+ }
+ },
+ "packages/octicons-react-symbols/node_modules/rollup-plugin-typescript2/node_modules/estree-walker": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz",
+ "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "packages/octicons-react-symbols/node_modules/universalify": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz",
+ "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 10.0.0"
+ }
+ },
"packages/postcss-preset-primer": {
"version": "0.0.0",
"dependencies": {
@@ -29766,7 +30033,7 @@
},
"packages/react": {
"name": "@primer/react",
- "version": "38.28.0",
+ "version": "38.29.0",
"license": "MIT",
"dependencies": {
"@github/mini-throttle": "^2.1.1",
@@ -29803,7 +30070,7 @@
"@figma/code-connect": "1.3.2",
"@primer/css": "^21.5.1",
"@primer/doc-gen": "^0.0.1",
- "@rolldown/plugin-babel": "^0.2.3",
+ "@primer/octicons-react-symbols": "^0.0.0",
"@rollup/plugin-babel": "6.1.0",
"@rollup/plugin-commonjs": "29.0.0",
"@rollup/plugin-json": "6.1.0",
@@ -29816,6 +30083,7 @@
"@storybook/addon-mcp": "^0.6.0",
"@storybook/icons": "^2.0.2",
"@storybook/react-vite": "^10.4.2",
+ "@tanstack/react-virtual": "^3.13.12",
"@testing-library/dom": "^10.4.0",
"@testing-library/jest-dom": "^6.4.5",
"@testing-library/react": "^16.3.0",
diff --git a/packages/octicons-react-symbols/.gitignore b/packages/octicons-react-symbols/.gitignore
new file mode 100644
index 00000000000..4150114be5f
--- /dev/null
+++ b/packages/octicons-react-symbols/.gitignore
@@ -0,0 +1 @@
+src/generated/**
diff --git a/packages/octicons-react-symbols/README.md b/packages/octicons-react-symbols/README.md
new file mode 100644
index 00000000000..1bc8ac7977b
--- /dev/null
+++ b/packages/octicons-react-symbols/README.md
@@ -0,0 +1,50 @@
+# @primer/octicons-react-symbols
+
+[](https://www.npmjs.com/package/@primer/octicons-react-symbols)
+[](
+
+> Optimized React components for rendering Octicons with shared SVG symbols
+
+## Getting started
+
+To install `@primer/octicons-react-symbols` in your project, you will need to
+run the following command using [npm](https://www.npmjs.com/):
+
+```bash
+npm install -S @primer/octicons-react-symbols
+```
+
+## Usage
+
+The `@primer/octicons-react-symbols` package provides Octicon components that
+reference shared SVG symbols instead of re-creating the same SVG markup every
+time an icon is rendered. This is useful in apps that render the same icons many
+times, because the symbol definition can be shared while each icon renders a
+small `` reference.
+
+At the root of your application, wrap your app with `OcticonSymbols`:
+
+```tsx
+import {OcticonSymbols, CheckSymbol} from '@primer/octicons-react-symbols'
+
+function RootLayout() {
+ return (
+ <>
+
+
+
+
+ >
+ )
+}
+```
+
+Downstream components can render icons normally:
+
+```tsx
+import {CheckIcon} from '@primer/octicons-react-symbols'
+
+function Status() {
+ return
+}
+```
diff --git a/packages/octicons-react-symbols/config/vitest/browser/setup.ts b/packages/octicons-react-symbols/config/vitest/browser/setup.ts
new file mode 100644
index 00000000000..70d4e41eb84
--- /dev/null
+++ b/packages/octicons-react-symbols/config/vitest/browser/setup.ts
@@ -0,0 +1,13 @@
+import {beforeEach} from 'vitest'
+import {cleanup} from '@testing-library/react'
+import '@testing-library/jest-dom/vitest'
+
+declare global {
+ var IS_REACT_ACT_ENVIRONMENT: boolean
+}
+
+beforeEach(() => {
+ cleanup()
+})
+
+globalThis.IS_REACT_ACT_ENVIRONMENT = true
diff --git a/packages/octicons-react-symbols/package.json b/packages/octicons-react-symbols/package.json
new file mode 100644
index 00000000000..9958faf485c
--- /dev/null
+++ b/packages/octicons-react-symbols/package.json
@@ -0,0 +1,52 @@
+{
+ "name": "@primer/octicons-react-symbols",
+ "version": "0.0.0",
+ "type": "module",
+ "private": true,
+ "exports": {
+ "types": "./dist/generated/index.d.ts",
+ "default": "./dist/generated/index.js"
+ },
+ "scripts": {
+ "build": "node script/build.ts && rolldown -c",
+ "clean": "rimraf dist",
+ "lint:npm": "publint --types",
+ "type-check": "tsc --noEmit",
+ "watch": "rolldown -c -w"
+ },
+ "dependencies": {
+ "@babel/runtime": "^7.28.4",
+ "react-compiler-runtime": "^1.0.0"
+ },
+ "devDependencies": {
+ "@babel/core": "^7.28.5",
+ "@babel/generator": "^7.28.5",
+ "@babel/plugin-transform-runtime": "^7.28.5",
+ "@babel/preset-react": "^7.28.5",
+ "@babel/preset-typescript": "^7.28.5",
+ "@primer/octicons": "^19.21.1",
+ "@rolldown/plugin-babel": "^0.2.3",
+ "@rollup/plugin-babel": "^7.1.0",
+ "@types/babel__core": "^7.20.5",
+ "@types/babel__generator": "^7.27.0",
+ "@vitejs/plugin-react": "^6.0.2",
+ "babel-plugin-react-compiler": "^1.0.0",
+ "change-case": "^5.4.4",
+ "publint": "^0.3.16",
+ "react": "^18.3.1",
+ "rimraf": "^6.1.3",
+ "rolldown": "^1.1.2",
+ "rollup-plugin-typescript2": "^0.37.0",
+ "typescript": "^6.0.3"
+ },
+ "peerDependencies": {
+ "@types/react": "18.x || 19.x",
+ "react": "18.x || 19.x"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ }
+ },
+ "sideEffects": false
+}
diff --git a/packages/octicons-react-symbols/rolldown.config.js b/packages/octicons-react-symbols/rolldown.config.js
new file mode 100644
index 00000000000..ee067401bd6
--- /dev/null
+++ b/packages/octicons-react-symbols/rolldown.config.js
@@ -0,0 +1,47 @@
+import {defineConfig} from 'rolldown/config'
+import babel from '@rollup/plugin-babel'
+import typescript from 'rollup-plugin-typescript2'
+import packageJson from './package.json' with {type: 'json'}
+
+const external = [
+ ...Object.keys(packageJson.peerDependencies ?? {}),
+ ...Object.keys(packageJson.dependencies ?? {}),
+ ...Object.keys(packageJson.devDependencies ?? {}),
+].map(name => new RegExp(`^${name}(/.*)?`))
+
+export default defineConfig({
+ input: ['./src/generated/index.ts'],
+ external,
+ plugins: [
+ typescript({
+ tsconfig: 'tsconfig.build.json',
+ }),
+ babel({
+ presets: [
+ '@babel/preset-typescript',
+ [
+ '@babel/preset-react',
+ {
+ runtime: 'automatic',
+ },
+ ],
+ ],
+ plugins: [
+ [
+ 'babel-plugin-react-compiler',
+ {
+ target: '18',
+ },
+ ],
+ '@babel/plugin-transform-runtime',
+ ],
+ extensions: ['.js', '.jsx', '.ts', '.tsx'],
+ babelHelpers: 'runtime',
+ }),
+ ],
+ output: {
+ dir: './dist',
+ preserveModules: true,
+ preserveModulesRoot: 'src',
+ },
+})
diff --git a/packages/octicons-react-symbols/script/build.ts b/packages/octicons-react-symbols/script/build.ts
new file mode 100644
index 00000000000..4f9f2e6da12
--- /dev/null
+++ b/packages/octicons-react-symbols/script/build.ts
@@ -0,0 +1,242 @@
+import fs from 'node:fs/promises'
+import path from 'node:path'
+import babel from '@babel/core'
+import {generate} from '@babel/generator'
+import {pascalCase} from 'change-case'
+import data from '@primer/octicons/build/data.json' with {type: 'json'}
+
+const {types: t} = babel
+const SOURCE_DIRECTORY = path.resolve(import.meta.dirname, '../src')
+const GENERATED_DIRECTORY = path.join(SOURCE_DIRECTORY, 'generated')
+
+await fs.mkdir(GENERATED_DIRECTORY, {recursive: true})
+
+// Each icon is around 2-3kB in size, create modules around 100kB in size
+const BUCKET_SIZE = 40
+const modules = partition(Object.values(data), BUCKET_SIZE).map((icons, index) => {
+ const filepath = path.join(GENERATED_DIRECTORY, `icons-${(index + 1).toString().padStart(2, '0')}.tsx`)
+
+ const propsImportSpecifier = t.importSpecifier(
+ t.identifier('OcticonReferenceProps'),
+ t.identifier('OcticonReferenceProps'),
+ )
+ propsImportSpecifier.importKind = 'type'
+
+ const imports = [
+ // import {forwardRef} from 'react'
+ t.importDeclaration(
+ [t.importSpecifier(t.identifier('forwardRef'), t.identifier('forwardRef'))],
+ t.stringLiteral('react'),
+ ),
+
+ // import {Icon} from '../Icon'
+ t.importDeclaration([t.importSpecifier(t.identifier('Icon'), t.identifier('Icon'))], t.stringLiteral('../Icon')),
+
+ // import type {OcticonReferenceProps} from '../types'
+ t.importDeclaration([propsImportSpecifier], t.stringLiteral('../types')),
+ ]
+
+ const components = icons.flatMap(icon => {
+ const symbolName = `${pascalCase(icon.name)}Symbol`
+ const symbols = Object.entries(icon.heights).map(([height, size]) => {
+ const id = `symbol-octicon-${icon.name}-${height}`
+
+ const jsx = t.jsxElement(
+ t.jsxOpeningElement(t.jsxIdentifier('symbol'), [
+ t.jsxAttribute(t.jsxIdentifier('id'), t.stringLiteral(id)),
+ t.jsxAttribute(t.jsxIdentifier('viewBox'), t.stringLiteral(`0 0 ${size.width} ${height}`)),
+ ]),
+ t.jsxClosingElement(t.jsxIdentifier('symbol')),
+ svgToJSX(size.ast),
+ )
+
+ return {
+ id,
+ height,
+ width: size.width,
+ jsx,
+ }
+ })
+ const symbolComponent = t.functionDeclaration(
+ t.identifier(symbolName),
+ [],
+ t.blockStatement([
+ t.returnStatement(
+ symbols.length === 1
+ ? symbols[0].jsx
+ : t.jsxFragment(
+ t.jsxOpeningFragment(),
+ t.jsxClosingFragment(),
+ symbols.map(symbol => symbol.jsx),
+ ),
+ ),
+ ]),
+ )
+
+ const referenceName = `${pascalCase(icon.name)}Icon`
+ const forwardRef = t.callExpression(t.identifier('forwardRef'), [
+ t.functionExpression(
+ t.identifier(referenceName),
+ [t.identifier('props'), t.identifier('ref')],
+ t.blockStatement([
+ t.returnStatement(
+ t.jsxElement(
+ t.jsxOpeningElement(
+ t.jsxIdentifier('Icon'),
+ [
+ t.jsxSpreadAttribute(t.identifier('props')),
+ t.jsxAttribute(t.jsxIdentifier('ref'), t.jsxExpressionContainer(t.identifier('ref'))),
+ t.jsxAttribute(
+ t.jsxIdentifier('sizes'),
+ t.jsxExpressionContainer(
+ t.objectExpression(
+ symbols.map(symbol =>
+ t.objectProperty(
+ t.stringLiteral(symbol.height),
+ t.objectExpression([
+ t.objectProperty(t.stringLiteral('width'), t.numericLiteral(symbol.width)),
+ t.objectProperty(t.stringLiteral('id'), t.stringLiteral(symbol.id)),
+ ]),
+ ),
+ ),
+ ),
+ ),
+ ),
+ ],
+ true,
+ ),
+ t.jsxClosingElement(t.jsxIdentifier('Icon')),
+ [],
+ ),
+ ),
+ ]),
+ ),
+ ])
+
+ forwardRef.typeParameters = t.tsTypeParameterInstantiation([
+ t.tsTypeReference(t.identifier('SVGSVGElement')),
+ t.tsTypeReference(t.identifier('OcticonReferenceProps')),
+ ])
+
+ const reference = t.variableDeclaration('const', [t.variableDeclarator(t.identifier(referenceName), forwardRef)])
+
+ return [t.exportNamedDeclaration(symbolComponent), t.exportNamedDeclaration(reference)]
+ })
+
+ const body = [...imports, ...components]
+ const program = t.addComment(
+ t.program(body),
+ 'leading',
+ `This file is auto-generated by 'script/build.ts'. Do not edit directly.`,
+ )
+
+ return {
+ filepath,
+ contents: generate(program).code,
+ exports: icons.flatMap(icon => [`${pascalCase(icon.name)}Icon`, `${pascalCase(icon.name)}Symbol`]),
+ }
+})
+
+for (const mod of modules) {
+ await fs.writeFile(mod.filepath, mod.contents)
+}
+
+const indexFilePath = path.join(SOURCE_DIRECTORY, 'generated', 'index.ts')
+
+const octiconsSymbolsPropsExportSpecifier = t.exportSpecifier(
+ t.identifier('OcticonSymbolsProps'),
+ t.identifier('OcticonSymbolsProps'),
+)
+octiconsSymbolsPropsExportSpecifier.exportKind = 'type'
+
+const octiconsReferencePropsExportSpecifier = t.exportSpecifier(
+ t.identifier('OcticonReferenceProps'),
+ t.identifier('OcticonReferenceProps'),
+)
+octiconsReferencePropsExportSpecifier.exportKind = 'type'
+
+const index = t.addComment(
+ t.program([
+ t.exportNamedDeclaration(
+ null,
+ [
+ t.exportSpecifier(t.identifier('OcticonSymbols'), t.identifier('OcticonSymbols')),
+ octiconsSymbolsPropsExportSpecifier,
+ ],
+ t.stringLiteral('../OcticonSymbols'),
+ ),
+ t.exportNamedDeclaration(null, [octiconsReferencePropsExportSpecifier], t.stringLiteral('../types')),
+ ...modules.map(mod => {
+ return t.exportNamedDeclaration(
+ null,
+ mod.exports.map(exportName => t.exportSpecifier(t.identifier(exportName), t.identifier(exportName))),
+ t.stringLiteral(`./${path.basename(mod.filepath, path.extname(mod.filepath))}`),
+ )
+ }),
+ ]),
+ 'leading',
+ `This file is auto-generated by 'script/build.ts'. Do not edit directly.`,
+)
+
+await fs.writeFile(indexFilePath, generate(index).code)
+
+function partition(items: Array, size: number): Array> {
+ const result: Array> = []
+ let bucket: Array = []
+ let count = 0
+
+ for (const item of items) {
+ if (count >= size) {
+ result.push(bucket)
+ bucket = []
+ count = 0
+ }
+
+ bucket.push(item)
+ count++
+ }
+
+ if (bucket.length > 0) {
+ result.push(bucket)
+ }
+
+ return result
+}
+
+type SVGASTNode = {
+ type: string
+ name: string
+ attributes: Record
+ children: Array
+}
+
+function svgToJSX(node: SVGASTNode): Array {
+ if (node.type === 'element') {
+ const children = node.children.map(svgToJSX)
+
+ if (node.name === 'svg') {
+ if (children.length === 0) {
+ throw new Error(`No children available for icon`)
+ }
+
+ return children.flat()
+ }
+
+ const attrs = Object.entries(node.attributes).map(([key, value]) => {
+ if (typeof value !== 'string') {
+ throw new Error(`Unknown value type: ${value}`)
+ }
+ return t.jsxAttribute(t.jsxIdentifier(key), t.stringLiteral(value))
+ })
+ const openingElement = t.jsxOpeningElement(t.jsxIdentifier(node.name), attrs, children.length === 0)
+ const closingElement = t.jsxClosingElement(t.jsxIdentifier(node.name))
+
+ if (children.length > 0) {
+ return [t.jsxElement(openingElement, closingElement, children.flat(), false)]
+ }
+
+ return [t.jsxElement(openingElement, closingElement, [], true)]
+ }
+
+ throw new Error(`Unknown type: ${node.type}`)
+}
diff --git a/packages/octicons-react-symbols/src/Icon.tsx b/packages/octicons-react-symbols/src/Icon.tsx
new file mode 100644
index 00000000000..b6cf554c9b1
--- /dev/null
+++ b/packages/octicons-react-symbols/src/Icon.tsx
@@ -0,0 +1,67 @@
+import {forwardRef, type HTMLAttributes} from 'react'
+import type {OcticonReferenceProps} from './types'
+
+const sizeMap = {
+ small: 16,
+ medium: 32,
+ large: 64,
+}
+
+type Size = 'small' | 'medium' | 'large'
+
+type IconProps = HTMLAttributes &
+ OcticonReferenceProps & {
+ size?: Size | number
+ sizes: Record<
+ string,
+ {
+ id: string
+ width: number
+ }
+ >
+ }
+
+const Icon = forwardRef(function Icon(
+ {'aria-label': ariaLabel, 'aria-labelledby': ariaLabelledBy, className, size = 16, sizes, tabIndex, title, ...rest},
+ ref,
+) {
+ const height = typeof size === 'number' ? size : sizeMap[size]
+ const heights = Object.keys(sizes)
+ const naturalHeight = closestNaturalHeight(heights, height)
+ const naturalWidth = sizes[naturalHeight].width
+ const width = height * (naturalWidth / naturalHeight)
+ const id = sizes[naturalHeight].id
+ const labelled = ariaLabel || ariaLabelledBy
+ const role = labelled ? 'img' : undefined
+
+ return (
+
+ )
+})
+
+function closestNaturalHeight(naturalHeights: Array, height: number): number {
+ const parsed = naturalHeights.map(naturalHeight => parseInt(naturalHeight, 10))
+
+ return parsed.reduce((acc, naturalHeight) => {
+ return naturalHeight <= height ? naturalHeight : acc
+ }, parsed[0])
+}
+
+export {Icon}
+export type {IconProps}
diff --git a/packages/octicons-react-symbols/src/OcticonSymbols.tsx b/packages/octicons-react-symbols/src/OcticonSymbols.tsx
new file mode 100644
index 00000000000..725d6da6843
--- /dev/null
+++ b/packages/octicons-react-symbols/src/OcticonSymbols.tsx
@@ -0,0 +1,44 @@
+import {createContext, Fragment, useMemo, useState} from 'react'
+import type {PropsWithChildren} from 'react'
+
+type OcticonSymbolsProps = PropsWithChildren>
+
+type OcticonSymbolsContextValue = {
+ registerSymbol(id: string, symbol: React.ReactNode): void
+}
+
+const OcticonSymbolsContext = createContext(null)
+
+function OcticonSymbols({children}: OcticonSymbolsProps) {
+ const [symbols, setSymbols] = useState