gl-react: typecheck is a TODO that exits clean, and headless GL is pinned to a prerelease
gl-react – React library to write and compose WebGL shaders
At a glance
- What is it?
- A React library for writing and composing WebGL shaders, shipped as a universal core plus four platform packages, with a component-per-effect model that leans on the React lifecycle for partial redraws. The tooling around it has some honest gaps: the typecheck script does nothing and says why, ten transitive dependencies are force-overridden, and the two React Native packages are described with the same copied sentence.
- Who is it for?
- gl-react is worth considering if you want shader effects expressed as React components with the redraw work tied to the component lifecycle, since that is the design idea the library is built around and the platform split means the same shader code can run in a browser, a native app or Node. Two things to check before you build on it.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 4 days ago.
- What is it written in?
- Mainly TypeScript, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 5, 2026, and from our analysis. They are not legal advice.
Editorial analysis
Two of the four platform packages carry the same description
The library is split into a universal core and four concrete implementations you must choose between: one for React DOM backed by WebGL, two for React Native, and one for Node.js backed by a headless GL binding. The two React Native entries are the problem. Their descriptions are the same sentence, down to the clause about being built on the Expo implementation over unimodules, which belongs to the first of the two but is repeated in the second. So the document does not actually distinguish the package that uses the Expo view directly from the one that layers it over native modules; a reader has to infer the difference from the package names and the links beside them. The other two entries are distinct: the DOM one says only that it is backed by WebGL, and the Node one names its binding. The chat link in the same list has no label at all, so it renders as an empty bullet.
The projects list is marked alphabetical and is not
The section listing projects that use the library carries an HTML comment instructing that entries stay in alphabetical order. The four entries below it are not alphabetical: a site beginning with one letter is followed by two beginning with others, and a name beginning with the last letter of the alphabet comes after a name beginning with the first. So the instruction is present, visible in the source, and violated by the list immediately beneath it. The list also ends with an italic invitation for readers to add their own project, which is the mechanism by which a hand-maintained list like this normally grows out of order. It is a small thing, and it is the kind of small thing that tells you how the rest of the document is maintained.
The example promises a rendered result that is not shown
The introductory example is complete up to its last step. It defines a shader with a fragment source in a tagged template, builds a component that passes a uniform, and mounts that component inside a surface element of a fixed size. The text then says this renders, followed by a colon. What comes after the colon is not in the file. So the one thing a reader cannot get from the documentation is what the result looks like, which for a graphics library is the part that matters most when you are deciding whether it fits. The same pattern appears earlier in miniature: a list of surface imports for each implementation is followed by a sentence introducing code, and that code is present. Only the final image is missing.
The Vite section is a workaround for a dependency reaching for Node's global
There is one build-tool section, and it is not optional. The library depends on a typed array pooling package that references Node's global object, which does not exist in a browser bundle, so the consumer is told to add a definition to their bundler configuration:
export default defineConfig({
// ...
define: {
global: "globalThis",
},
});The substitution tells the bundler to replace the identifier with its modern equivalent at build time, which is a workaround rather than a fix inside the dependency. It is also the only configuration the library asks of you: there is no note about transpilation, no peer version ranges for React, and no requirement beyond this substitution. Worth knowing that a plain bundler setup needs this line, because a missing definition surfaces as an undefined global at runtime rather than as a build error, which is a confusing way to discover a configuration requirement. The readme also mentions support for a type annotation format from the Flow ecosystem, which is a leftover from before the codebase moved to TypeScript and is one of the few features entries that describes something the current sources do not use.
The typecheck script prints a todo and exits successfully
The root manifest has a typecheck script, and what it does is print a message and stop: `echo 'TODO: tsc -b (needs peerDep resolution for PnP)'`. The stated blocker is that the compiler's build mode needs peer dependency resolution that the package manager's Plug'n'Play layout does not provide. So the command is a placeholder that reports nothing and exits with success. Any pipeline that treats a passing typecheck as a gate is currently gating on nothing, and the message is the only warning. The rest of the script set is real: build and watch delegate to shell scripts, tests run in a separate package and have a companion that rewrites snapshots, clean removes installed trees and built output, and release builds before publishing through the changeset tool.
Ten transitive dependencies are force-overridden, one to a prerelease
The package manager configuration ends with a block of overrides, ten entries that replace whatever version a transitive dependency would otherwise resolve to. Most of them push a library up to a much newer major than its dependents normally ask for, which is the shape you get when the reason is a fix rather than a feature. The one that stands out is different in kind: the headless GL binding is pinned to an exact prerelease version with no range at all. So the package that powers the Node implementation depends on a release candidate of a native module, pinned precisely, while the browser implementation has no equivalent constraint. Nothing in the document explains the pin, and a native module at a candidate version is where a platform break is most likely to appear.
The install guard matches a substring, and the formatter skips a package
Two small mechanisms show how the workspace is policed. The first is a preinstall hook that runs a one-line script which looks for the package manager's name inside the path of whatever invoked the install, and exits with an error if it is absent. The rejection message is printed in red using terminal escape codes embedded in the string, so the guard is not just advisory, it is loud. The second is the format script, which passes a glob naming five of the library packages and the cookbook to the formatter. The Expo cookbook is not in that list, although the readme links it as one of the things to look at. So a contributor working in that package gets no formatting from the root command and has to run the tool themselves.
Four years separate the last v5 release from v6
The release history has a hole in it. The tags visible are v6.0.0 from June 2026, v5.2.0 from May 2022, and v5.1.0 from March 2021. So the project went from a 1.0 release to a 2.0 one in a year, then sat for four years before cutting a major version, and the last push to the repository is dated 2026-10-01. The readme is titled for version 6 and its guidance is current, so the documentation caught up with the release rather than describing a version in between. What the gap does mean is that anything written against the 5.x line is four years older than the current major, and the readme offers no upgrade notes for crossing it.
Editorial conclusion
gl-react is worth considering if you want shader effects expressed as React components with the redraw work tied to the component lifecycle, since that is the design idea the library is built around and the platform split means the same shader code can run in a browser, a native app or Node. Two things to check before you build on it. The root typecheck script reports success without checking anything, so a green typecheck in continuous integration means only that the command ran. And the package manager configuration pins the headless GL binding to an exact release candidate rather than a stable release, so the Node implementation depends on a prerelease of its native dependency while the browser path does not.
Frequently asked questions
What is gl-react?
A React library to write and compose WebGL shaders, so that complex effects are built by composing components rather than by writing imperative graphics code. It is MIT licensed, currently at version 6, and couples with React's lifecycle so that only a component update triggers a redraw.
Which platforms does gl-react support?
A universal core must be paired with one of four implementations: one for React DOM backed by WebGL, two for React Native backed by an Expo view, and one for Node.js backed by a headless GL binding. The readme describes the two React Native entries with the same sentence, so the distinction between them is left to the package names.
How do I use gl-react with Vite?
Add a substitution to your bundler configuration. The library depends on a typed array pooling package that references Node's global, so the readme tells you to define that identifier as globalThis in your config, otherwise it is undefined at runtime.
Does gl-react have a working typecheck script?
Not yet. The root typecheck script prints a todo message and exits successfully, giving the reason that the compiler's build mode needs peer dependency resolution which the Plug'n'Play layout does not provide. A passing typecheck therefore checks nothing.
What is the release history of gl-react?
v6.0.0 from June 2026, v5.2.0 from May 2022 and v5.1.0 from March 2021, so four years separate the last v5 release from v6. The last push to the repository is dated 2026-10-01, and the readme carries no upgrade notes for crossing that gap.
How do I run the gl-react examples?
The cookbook package can be run locally with pnpm cookbook, an Expo cookbook is linked separately, and a hello example exists as a JSFiddle. The root package is private and versioned zero, and its preinstall hook refuses to run unless the install was invoked through pnpm.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/gre-gl-react)