Use ordinary npm packages and React

Depend on packages that know nothing about Beyond, such as React, and understand how peers, versions and unsupported inputs are handled.

  • Availability: Planned
  • Evidence: Recorded run
  • How-to guide

Starting state

Your application depends on packages that were not written for Beyond. Ordinary npm packages are required inputs of the CDN, and React is the reference case.

How a package is recognized

A manifest without the beyond.publication field is an ordinary npm package:

JSON
{ "name": "react", "version": "18.3.1", "main": "index.js", "exports": { ".": "./index.js" } }

A present but invalid field is an error, never a fallback to this case.

Its public modules are what the package itself exposes: its root entry and the subpaths of its exports. A file that merely exists inside the archive is not a public module, and you cannot address it through the CDN.

How exports is read

Package shape Handling
exports with subpath keys, the root shorthand, nested conditions, default, null exclusions, fallback arrays and single-* patterns Supported. Conditions are tried in the order the package wrote them. The active conditions are browser or node, import, module, default and the environment.
No exports browser (string form, browser target only), then module, then main, then ./index.js
A CommonJS entry, as in React Supported. Export names are read statically, following module.exports = require('./file') through every environment branch. The unit exports those names and default.
require('bare') inside CommonJS Supported. It becomes an import of the same bare specifier, so a renderer and the library it peers on share one public module.
A package that only offers require targets Still resolved; its output is adapted

Each public module is one unit. React is not bundled into your code, and it is not split further: react and react-dom/client are separate units that reference each other by their public specifiers.

Add it to your application

Name the package in the selections of a registration, with an exact version or a range:

JSON
{
  "selections": [
    { "package": "react", "selection": "^18.3.0" },
    { "package": "react-dom", "selection": "^18.3.0" }
  ]
}

A range is resolved once, when the registration runs, and the exact version is pinned in the graph. It does not move until you register again.

One React, not two

Libraries declare React as a peer dependency, because an application must run exactly one copy of it. A peer is not installed by the package that declares it: it is provided by the nearest dependent, from the direct dependent up to your application, that depends on it. Its range constrains the version that provider selects, so react@* together with a renderer that requires ^18.3.1 selects 18. Your renderer and every library that uses React bind to the same version. An unmet required peer fails the registration; peers are never installed automatically, because that would hide a conflict you need to see before publishing. You can see the result before preparing anything:

  • the graph's edges record each peer binding;
  • a graph diff lists peers that changed or are in conflict.

When two parts of the graph genuinely need different versions of one package, the resolution keeps them apart with scopes. That is correct for ordinary libraries and wrong for React, so treat a peer conflict on react as something to fix with a selection or an override, not to ship.

Limits

Public packages stay public when your application is private: react is delivered publicly, under its own URL, to everyone.