Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
11b600d
Run CI on major version branches
tanem Jul 29, 2026
efc3023
Add "use client" to published bundles
tanem Jul 29, 2026
be8e293
Gate builds with publint and arethetypeswrong
tanem Jul 29, 2026
427dca3
Drop UMD builds
tanem Jul 30, 2026
824f650
Drop prop-types and collapse CJS builds
tanem Jul 30, 2026
b3b06e7
Build with tsdown and add an exports map
tanem Jul 30, 2026
8fffced
Rewrite as a function component and raise the React floor to 16.8
tanem Jul 30, 2026
7726d24
Use arrow functions for the forwardRef render functions
tanem Jul 30, 2026
a0248f1
Gate bundle size with size-limit
tanem Jul 30, 2026
a729cf8
Add "When To Use This" and "Security" sections to the README
tanem Jul 30, 2026
1cd47b3
Move agent instructions to AGENTS.md
tanem Jul 30, 2026
4607f7b
Fix the matrix run snippet and document the release labels in AGENTS.md
tanem Jul 30, 2026
27a1c46
Cut AGENTS.md down to what the code doesn't already say
tanem Jul 30, 2026
86bb77b
Drop the engines field
tanem Jul 30, 2026
7bd0dff
Serve the extensionless SVG as image/svg+xml in the no-extension example
tanem Jul 30, 2026
9cfd206
Let vite 404 the missing SVG in the fallbacks and loading examples
tanem Jul 30, 2026
c53f162
Size the injected sprite icons in the sprite-usage example
tanem Jul 30, 2026
f378ed5
Fix renovate's labels and drop its commit prefixes
tanem Jul 30, 2026
bdbfee5
Document the commit, PR title and wrapping conventions
tanem Jul 30, 2026
24fe4c0
Drop the em-dash preference from AGENTS.md
tanem Jul 30, 2026
9860947
Keep the dist-dependent specs out of the development loop
tanem Jul 30, 2026
2b39871
Note that ReactSVG is no longer usable in a type position
tanem Jul 30, 2026
2947e4d
Restructure the README API section
tanem Jul 30, 2026
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
75 changes: 0 additions & 75 deletions .github/copilot-instructions.md

This file was deleted.

4 changes: 2 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@ name: CI

on:
push:
branches: [master]
branches: [master, 'v*']
pull_request:
branches: [master]
branches: [master, 'v*']

concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
Expand Down
1 change: 0 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
.DS_Store
.vscode
build
compiled
coverage
dist
examples/*/package-lock.json
Expand Down
1 change: 0 additions & 1 deletion .prettierignore
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
*.md
.next
build
compiled
coverage
dist
package.json
93 changes: 93 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
# AGENTS.md

Rules for coding agents that the code and config don't already state. Keep it
that way: a constraint that can live in a comment next to the thing it
constrains belongs there, not here.

## Writing

- NZ English everywhere ("colour", "behaviour", "initialise").
- Single-line commit messages, `git log --oneline` style. Add a body only to
explain why, and only for behaviour or type changes.
- No conventional-commit prefixes (`feat:`, `fix:`, `chore(deps):`) in commit
subjects or PR titles. Write a plain capitalised sentence. Nothing reads the
prefix: the version bump comes from the PR label, and renovate is set to
`semanticCommits: "disabled"` to match.
- PR titles are copied verbatim into `CHANGELOG.md`, so write them as the
changelog line you want readers to see.
- Hard-wrap commit message bodies at 72 columns; `git log` does not reflow
them. Do not hard-wrap PR or issue descriptions: GitHub reflows markdown,
and its web editor leaves wrapped source ragged once anyone edits it.

## Architecture

Injection happens in a single effect in `src/ReactSVG.tsx`. The two-wrapper
structure, outer managed by React and inner managed by `@tanem/svg-injector`,
is load-bearing: don't collapse it.

That file's comments cover the rest: why `forwardRef` is required, why the
effect's dependency list is deliberately narrow, why the callbacks are read
through a ref, and what the teardown guard protects. Read them before changing
the injection flow.

## Build & test

`npm run test:src` is the development loop. `npm test` is the full gate.
`npm run test:react` runs the React matrix and is slow enough to be
pre-release only. `npm run size`, `npm run test:dist` and the `package:*`
checks read `dist/`, so they need a current `npm run build`. A new spec that
reads `dist/` goes in `config/jest/dist-tests.js`, which keeps it out of
`test:src`.

Give each test its own `faker.seed()` and a `faker.string.uuid()` SVG URL, or
svg-injector's cache leaks state between tests. Injection is async: assert
through `await waitFor(...)`.

Raising a `size-limit` budget in `package.json` is a decision, not a fix. Find
what grew first, and say why in the commit message.

The React matrix covers boundary versions only: the first and last minor of
each supported major, plus minors that changed behaviour. Currently 16.8,
16.14, 17.0, 18.0, 18.3, 19.0, 19.1. Adding a boundary means replacing the
previous last-minor for that major, not accumulating versions. Copy a sibling
`test/react/<version>/package.json`, and see `scripts/test-react.ts` for how a
single version is run.

## Releases

`npm run release` runs on a Monday cron against `master`. It takes the version
bump from the labels on PRs merged since the last tag, then regenerates
`CHANGELOG.md` and `AUTHORS` and bumps `version` in `package.json` and
`package-lock.json`.

- Exactly one label per PR. None, or more than one, throws and blocks the
release for everything merged alongside it. `breaking` gives a major,
`enhancement` a minor, `bug` / `documentation` / `internal` a patch. Tooling,
CI and dependency work is `internal`.
- Never hand-edit `CHANGELOG.md`, `AUTHORS` or either `version` field.
- Breaking changes need a `MIGRATION.md` entry in the same PR: the generated
changelog is only a list of PR titles.

## Dependencies

Pin `devDependencies` to exact versions. Keep `dependencies` on caret ranges.

## Examples

`examples/` are built to open on CodeSandbox, so their platform dependencies
(vite, @vitejs/plugin-react, next, typescript, @types/react, @types/react-dom)
track the official
[sandbox-templates](https://github.com/codesandbox/sandbox-templates/tree/main):
`react-vite` / `react-vite-ts` for the Vite examples, `nextjs` for the SSR one.
Don't bump those past the template, except for patch-level security fixes
inside the template's major.minor. Example-only dependencies
(`styled-components`, `glamor`, `react-frame-component`) aren't governed by it.

Renovate skips `examples/**`, so updates are manual: do every example in one
commit and check at least one still opens on CodeSandbox.

## Conventions

- `src/types.ts` is the only prop contract. There is no runtime `propTypes`.
- Keep `Props` flat. It extends `HTMLAttributes` and `SVGAttributes`, and
nesting it trips excessive-depth errors in wrapper libraries.
1 change: 1 addition & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
@AGENTS.md
25 changes: 25 additions & 0 deletions MIGRATION.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,31 @@

Details relating to major changes that aren't presently in `CHANGELOG.md`, due to limitations with how that file is being generated.

## v18.0.0

**Added**

- An `exports` map. `react-svg` and `react-svg/package.json` are the only entry points; paths into `dist` are no longer reachable, even though the top-level `main`, `module` and `types` fields are still set for webpack 4 and TypeScript `node10` resolution. Node ESM consumers now get the ES module build rather than falling back to CommonJS.
- `sideEffects: false`, so bundlers can drop the package entirely when nothing is imported from it.

**Changed**

- The minimum supported React version is now 16.8, up from 16.0. The peer dependency range is `^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0`. React's support unit is the major, and fixes for the 16.x line only ever land on 16.14.x, so individual pre-16.8 minors were never separately supported.
- `ReactSVG` is a function component built on hooks, rather than a class component. `defaultProps` is gone (React 19 ignores it on function components); prop defaults are unchanged and are now applied by destructuring.
- `ReactSVG` is a value only, so it can no longer be used in a type position. A class declaration doubles as a type describing its instances, which made `Omit<ReactSVG, 'src'>` and similar valid; the same code now fails with `TS2749: 'ReactSVG' refers to a value, but is being used as a type here`. Use the exported `Props` type instead: `Omit<Props, 'src'>`.
- `ref` now resolves to the outermost wrapper DOM element - an `HTMLDivElement`, `HTMLSpanElement` or `SVGSVGElement`, depending on `wrapper` - instead of the `ReactSVG` class instance. The class instance had no documented methods, so the DOM node is the useful thing to hand back. Type the ref as the exported `WrapperType` if you need it.
- Re-injection now only happens when a prop that affects the injected SVG changes: `src`, `wrapper`, `title`, `desc`, `evalScripts`, `httpRequestWithCredentials`, `renumerateIRIElements` or `useRequestCache`. Previously any prop change re-fetched and re-injected, including ones that only apply to the React wrapper (`className`, `style`, event handlers) and inline `beforeInjection` / `afterInjection` / `onError` functions, whose identity changes on every render. Those callbacks are still always invoked in their latest form; they just no longer trigger an injection by themselves. If you were relying on a wrapper prop change to force a re-injection, change `src` instead.
- Build output filenames. The CommonJS build is `dist/react-svg.cjs` (was `dist/react-svg.cjs.js`) and the ES module build is `dist/react-svg.mjs` (was `dist/react-svg.esm.js`). Type declarations are `dist/react-svg.d.cts` and `dist/react-svg.d.mts` (was `dist/index.d.ts` plus one file per source module). Importing `react-svg` is unaffected.
- The build pipeline moved from TypeScript plus Rollup and Babel to [tsdown](https://tsdown.dev). Output still targets ES2019. `@babel/runtime` is no longer a runtime dependency, leaving `@tanem/svg-injector` as the only one.
- `src` is now published alongside `dist` so the declaration maps resolve.

**Removed**

- The `State` type export. It described the internal state shape of the class component, which no longer exists.
- `propTypes` validation. TypeScript types are the supported contract for props. React 19 ignores `propTypes` entirely, so this only changes behaviour for React 18 and earlier in development mode, where invalid props previously logged a console warning. `prop-types` and `@types/prop-types` are no longer dependencies.
- The separate development and production CommonJS builds. `dist/react-svg.cjs.development.js`, `dist/react-svg.cjs.production.js` and the `dist/index.js` shim that switched between them on `process.env.NODE_ENV` are replaced by a single unminified CommonJS build. With `propTypes` gone the two builds differed only by minification, which bundlers apply themselves.
- UMD builds. `dist/react-svg.umd.development.js` and `dist/react-svg.umd.production.js` are no longer published, and the `ReactSVG` browser global is gone. React itself stopped shipping UMD builds in v19, so script-tag usage already required pinning React 18 or earlier. If you load `react-svg` via a script tag, pin `react-svg@^17`, or switch to the ES module build with an import map or a bundler.

## v17.0.0

**Changed**
Expand Down
Loading
Loading