npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

react-app-element

v0.1.0

Published

Custom element base class for embedding full React applications in any host page

Readme

react-app-element

A custom element base class for mounting full React applications inside any HTML page or host application — without the host running a React build step.

Use it when you ship a self-contained React bundle and need a stable, framework-agnostic tag (<my-app>) that non-React pages can drop in. The library itself is tiny: React and ReactDOM are peer dependencies loaded by the host page (or your app shell), not bundled into this package.

Prior art / inspiration: r2wc, remount. This project targets a different slice of the problem — see How this differs from r2wc.

Install

npm install react-app-element
npm install react react-dom   # peer dependencies — same major as your app

From source (before a release hits npm, or for local development):

git clone https://github.com/dvegap95/react-app-element.git
cd react-app-element
npm install && npm run build
# in your consuming project:
npm install file:../path/to/react-app-element

Host page: load React first

Because React is externalized, the page that hosts your custom element must provide React before your element bundle runs.

CDN (quick test):

<script crossorigin src="https://unpkg.com/react@18/umd/react.production.min.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@18/umd/react-dom.production.min.js"></script>
<script type="module" src="/dist/my-app-element.js"></script>

Bundled host (Vite / webpack): mark react and react-dom as externals or rely on your bundler’s shared vendor chunk so only one React instance exists on the page.

Important: the React version on the host should match the version you built your app against (currently ^18.3).

Quick start

import { ReactEmbed } from 'react-app-element';
import App from './App';

class MyAppElement extends ReactEmbed {
  static get Component() {
    return App;
  }

  static get attributesMap() {
    return { 'base-path': 'basePath' };
  }

  static get propertiesMap() {
    return {
      locale: 'locale',
      onReady: 'onReady',
    };
  }
}

customElements.define('my-app', MyAppElement);
<my-app
  base-path="/apps/my-app/"
  locale="en"
  stylesheet="/apps/my-app/styles.css"
  styles-dir="/apps/my-app/assets"
  use-shadow-dom="true"
></my-app>

Declarative maps

| Static getter | Purpose | |---------------|---------| | attributesMap | HTML attributes (strings) → React props. Keys must be lowercase. | | propertiesMap | JS properties on the element → React props (including on* handlers). | | transformAttributes | Per-attribute parse / stringify (or a parse function). Defaults to JSON try/parse. | | Component | Root React component (usually your entire app). |

Shadow DOM and styles

  • Pass use-shadow-dom="true" or new MyAppElement(true) to render inside an open shadow root.
  • stylesheet — single CSS file URL injected as <link rel="stylesheet">.
  • styles-dir — prefix for additional chunk CSS files passed in the constructor’s styleFiles array.
  • ReactEmbedConfigContext exposes styleRoot (ShadowRoot or document.head) so Emotion / other CSS-in-JS can target the correct insertion point.

Imperative updates from outside React

Attribute and property changes dispatch an internal props event; EventManager merges updates into the mounted tree. Host scripts can set element.locale = 'es' or element.setAttribute('base-path', '/v2/') without re-mounting.

API

import {
  ReactEmbed,
  ReactEmbedConfigContext,
  type AttributesMap,
  type TransformAttributesMap,
  type ReactEmbedConfigContextType,
} from 'react-app-element';

ReactEmbed is the default export name re-exported as a named export to keep the published bundle free of mixed default/named Rollup warnings.

Development

npm install
npm test
npm run build   # emits dist/main.js (~few KB, React external)

Publishing (npm Trusted Publishing)

Releases use OIDC trusted publishing from GitHub Actions — no long-lived NPM_TOKEN in secrets.

One-time npm setup

  1. Publish 0.1.0 once interactively if the package does not exist yet (npm loginnpm publish).
  2. On npmjs.comSettings → Trusted publishingGitHub Actions:
    • Organization or user: dvegap95
    • Repository: react-app-element
    • Workflow filename: ci.yml (filename only)
    • Allowed actions: npm publish
  3. Optional hardening: Settings → Publishing access → require 2FA and disallow tokens.

Release from GitHub

Tag (recommended):

git tag v0.1.0
git push origin v0.1.0

Manual dispatch: Actions → CIRun workflow → check Publish to npm → Run.

The publish job runs only after test passes. Provenance is generated automatically via OIDC.

How this differs from r2wc

| | react-app-element | @r2wc/react-to-web-component | |---|------------------------|----------------------------------| | API shape | class MyEl extends ReactEmbed + static maps | r2wc(Component, { props, shadow, events }) factory | | Typical target | Whole app / micro-frontend shell | Single presentational component | | Prop wiring | attributesMap, propertiesMap, transformAttributes on the subclass | props: ['foo', 'bar'] or { foo: 'string' } config object | | Styling story | Shadow DOM toggle, external CSS URLs, styleRoot context for CSS-in-JS | Optional shadow: 'open'; no built-in stylesheet injection | | Bundle | Peer react / react-dom (host supplies) | ~1–2 KB wrapper; peers React | | Ecosystem | Standalone prior work / portfolio | De-facto standard, Bitovi-backed, very high npm adoption |

Overlap (honest): both create a custom element, observe attributes, and call createRoot().render(). For wrapping one component with a handful of props, r2wc is simpler and battle-tested.

Where this project pushes further today: subclass + maps scale better when one element owns an entire app with many host-driven knobs; shadow + linked stylesheets + ReactEmbedConfigContext target embed scenarios (legacy host, CMS, PHP shell) rather than “turn Button into <my-button>”.

Future work

Not planned for the initial publish — documented so direction stays clear:

  • [ ] Light DOM children → React — project slotted / child nodes into the React tree (declarative composition from host HTML).
  • [ ] Recursive custom elements — nested tags that each extend ReactEmbed, with maps composed or inherited down the tree.
  • [ ] Custom Elements Manifest — generate CEM from attributesMap / propertiesMap for IDE and docs tooling.
  • [ ] Generic typing — infer React props from maps instead of any on the element instance.
  • [ ] Demo site — minimal host page (CDN React) + one full app element example.
  • [ ] CEM + Storybook — document elements defined in consuming apps, not only this base class.
  • [ ] Optional default props — static defaultProps merged before attribute/property layers.
  • [ ] Disconnect / reconnect — explicit disconnectedCallback root teardown and re-mount policy.

Contributions welcome — open an issue or PR on GitHub.

Related

License

MIT