Piral packages

Piral ships as a small stack of layered packages. They share one runtime and one API; the difference is how much opinion comes pre-wired. Pick the outermost layer you need, and move inward as your app matures.

piral — all-in-one: core + standard plugins, opinionated piral-core — React runtime, extension slots, base Pilet API · you pick plugins piral-base — pilet loading & lifecycle runtime, no UI framework use directly only for a custom non-React runtime
Each layer wraps the previous one. Start with piral; graduate to piral-core for the long run.
PackageWhat it isReach for it when
piralThe opinionated, batteries-included framework packageGetting started, prototypes, small apps
piral-coreThe lean React runtime; you choose the pluginsMid- to long-term, production apps that want control
piral-baseThe framework-agnostic pilet loaderBuilding a custom, non-React runtime

piral — the opinionated starting point

piral is the fastest way to a working app shell. It re-exports everything from piral-core and piral-base and bundles a curated set of standard plugins and sensible defaults, so the common capabilities are already wired up.

import { createInstance } from 'piral';

const instance = createInstance({
  requestPilets: () => fetch(FEED_URL).then((r) => r.json()).then((r) => r.items),
});

Out of the box you get createInstance, the root rendering component, ExtensionSlot, the standard plugin components and APIs (menu, notifications, modals, dashboard, …), and the hooks (useGlobalState, useAction, useExtensions, usePilet). It's opinionated about what's included but leaves rendering and layout entirely to you. See createInstance for the full options.

piral-core — the choice for the long run

piral-core is the same runtime and the same API surface, but unopinionated about which plugins are included. You install and register exactly the capabilities you use — nothing more.

That explicitness is exactly what you want as an application matures:

  • Smaller, intentional footprint — no plugins you don't use.
  • No surprises on upgrade — behavior is whatever you registered, not whatever the meta-package decided to bundle.
  • Clear dependency graph — each capability traces to a package you chose.

For these reasons, piral-core is the recommended target for mid- to long-term and production applications. piral is the great on-ramp; piral-core is where serious apps tend to settle.

Migrating from piral to piral-core

The migration is mechanical and low-risk, because the runtime and API are identical — you're just making the implicit explicit.

1. Swap the dependency. Remove piral; add piral-core plus the plugin packages you actually use, and make sure React and React Router are direct dependencies.

npm uninstall piral
npm install piral-core piral-menu piral-notifications piral-modals \
  react react-dom react-router react-router-dom

2. Update imports. Pull the runtime from piral-core, and each plugin factory from its own package (with piral these came along for free).

- import { createInstance, createMenuApi, createNotificationsApi } from 'piral';
+ import { createInstance } from 'piral-core';
+ import { createMenuApi } from 'piral-menu';
+ import { createNotificationsApi } from 'piral-notifications';

3. Register the plugins explicitly. Anything piral set up for you now goes into the plugins array.

const instance = createInstance({
  plugins: [createMenuApi(), createNotificationsApi(), createModalsApi()],
  requestPilets: () => fetch(FEED_URL).then((r) => r.json()).then((r) => r.items),
});

4. Verify the Pilet API. Every method your pilets call must come from a plugin you registered. If a pilet uses api.showNotification but you didn't add piral-notifications, add it. This audit is the point of the move: each capability becomes intentional.

The result behaves identically to your piral app — just leaner and fully under your control.

A pragmatic path

Start on piral to move fast and prove the idea. Plan to graduate to piral-core once the app and team mature and you want a tighter, explicit footprint. Because the API is the same, the switch is a dependency-and-imports change, not a rewrite.

piral-base — the low-level loader

piral-base is the foundation both other packages build on: it fetches, evaluates, and manages the lifecycle of pilets, with no dependency on React or any UI framework. You'd use it directly only when building a completely custom, non-React runtime. For anything React-based, you'll be on piral-core (or piral), which already include it.

Converter packages

Framework converters (piral-vue, piral-svelte, piral-ng, …) are a special case worth calling out. They're published as plugin packages, but a converter should not be installed as a plugin in the app shell — that would make every pilet depend on the shell having the right converter.

Instead, each converter also ships a standalone convert submodule that a pilet imports and uses directly:

import { fromSvelte } from 'piral-svelte/convert';

api.registerPage('/profile', fromSvelte(ProfilePage));

Used this way, the converter carries its own logic inside the pilet and emits a plain HTML component the core already knows how to render — so the shell stays framework-agnostic and the pilet stays portable. Reach for the /convert submodule in pilets and avoid the plugin form for converters. See Creating a converter and Multi-framework pilets.

Shared API surface

Whichever layer you choose, the building blocks are the same — these all come from piral and piral-core alike:

import {
  createInstance,        // create the runtime
  ExtensionSlot,         // render registered extensions
  useGlobalState,        // read global state
  useAction,             // dispatch a state action
  useSharedData,         // read the shared data store
  usePiletApi,           // the root pilet API (shell-only)
} from 'piral-core';

import type {
  PiletApi,              // the typed pilet API
  PiralPlugin,           // plugin factory type
  PiletMetadata,         // feed response item
  LayoutProps,           // Layout component props
  ComponentsState,       // replaceable shell components
  ErrorComponentsState,  // error page components
} from 'piral-core';