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

@aircloudhq/web

v1.1.0

Published

The application package of an Air-Cloud product: the Rails Action View helpers, i18n over the product's locale bundle, resource pages over its models, and React bindings, for its site and its JavaScript functions.

Readme

@aircloudhq/web

The application package of an Air-Cloud product: what a product's site and its JavaScript functions import to render pages, format values and speak the product's languages. It brings the Rails Action View helpers to the browser and to functions, one module per Rails helper module, under the names a Rails developer already knows (numberToCurrency, timeAgoInWords, t, l, and the LinkTo and FormWith components).

  • Pages from your models. resourcePage("pet") renders list, show, create, edit and delete for an entity from the resource projection the platform derives from db/schema/pet.toml. Adding a field to the schema changes the page with no edit to the view.
  • I18n. Translations come from the locale bundle the platform compiles from config/locales/, with Rails' lazy lookup: t(".title") in pages/home/index.jsx reads home.index.title. Plurals, dates, numbers and currencies follow CLDR for every locale the product declares.
  • The same helpers in functions. @aircloudhq/web/function gives a function the caller's locale, so its error messages answer in the language of the request.
  • React bindings. @aircloudhq/web/react carries the router, layouts, links and form components.

A product created with aircloud new already depends on this package, at the version the platform builds and tests with. You rarely add it by hand.

Install

npm install @aircloudhq/web

It needs the Node.js release its engines field names (the platform's pinned line), and React 19.2.7 or later for the React bindings.

Usage

The site entry point aircloud new generates (app/site/src/main.jsx) renders the product's routes inside its layouts, with the locale bundle and the resource projection:

import { StrictMode } from "react";
import { createRoot } from "react-dom/client";
import { I18nProvider, SiteRouter } from "@aircloudhq/web/react";
import bundle from "../.aircloud/locale-bundle.json";
import projection from "../.aircloud/resource-projection.json";
import routes from "./routes.js";

// Every src/layouts/<name>.jsx, by name (`application` is the default layout).
const layouts = Object.fromEntries(
  Object.entries(import.meta.glob("./layouts/*.jsx", { eager: true })).map(([file, module]) => [
    file.slice(file.lastIndexOf("/") + 1, -4),
    module.default,
  ]),
);

createRoot(document.getElementById("root")).render(
  <StrictMode>
    <I18nProvider bundle={bundle}>
      <SiteRouter routes={routes} layouts={layouts} projection={projection} />
    </I18nProvider>
  </StrictMode>,
);

A page reads its strings by lazy lookup (app/site/src/pages/home/index.jsx):

import { useT } from "@aircloudhq/web/react";

export default function HomePage() {
  const t = useT();
  return (
    <section>
      <h1>{t(".title")}</h1>
      <p>{t(".intro")}</p>
    </section>
  );
}

The resource page aircloud generate scaffold pet writes is one line (app/site/src/pages/pet/index.jsx):

import { resourcePage } from "@aircloudhq/web/react";

export default resourcePage("pet");

Routing inside a page uses the router primitives @aircloudhq/web/react re-exports — Link, NavLink, Routes, Route, Outlet, Navigate, useLocation, useNavigate, useParams, useSearchParams — the same React Router instance the SiteRouter renders with. Import them from @aircloudhq/web/react, never from react-router: a second copy of the router breaks its context.

Dates and times render through l (useI18n().l, or l from @aircloudhq/web/helpers/translation), never Intl. A named format is looked up by what the value is:

  • an instant — a Date or a timestamp — reads time.formats.<name> and renders in the product's time zone;
  • a civil date — a "YYYY-MM-DD" string — reads date.formats.<name>.

So l(new Date(), { format: "weekday" }) reads time.formats.weekday; for date.formats.weekday pass the civil date (l("2026-03-08", { format: "weekday" })). A format no locale of the chain defines renders humanized and is reported, like a missing key: with a release, to the product host; with none (a dev server, a preview), I18nProvider warns on the console, naming the rule.

A function answers in the caller's locale (app/functions/pet/index.js):

import { i18nFor } from "@aircloudhq/web/function";

async function methodNotAllowed(request) {
  // functions.pet.errors.method_not_allowed, in the locale the request negotiates
  const i18n = await i18nFor(request, { function: "pet" });
  return Response.json({ error: { code: "method_not_allowed", message: i18n.t(".errors.method_not_allowed", { method: request.method }) } }, { status: 405 });
}

Modules

| Import | What it carries | |---|---| | @aircloudhq/web/react | SiteRouter, I18nProvider, useT, useSite, LinkTo, FormWith, resourcePage, the data hooks useList / useRecord, and the router primitives (Link, Routes, useParams, …) | | @aircloudhq/web/helpers/number | numberToCurrency, numberToPercentage, numberWithDelimiter, … | | @aircloudhq/web/helpers/date | timeAgoInWords, distanceOfTimeInWords, distanceOfTimeInWordsToNow | | @aircloudhq/web/helpers/text, …/tag, …/url, …/form, …/sanitize, …/navigation | the Action View helper modules of the same name | | @aircloudhq/web/helpers/translation | t and l | | @aircloudhq/web/i18n | createI18n, the runtime of the locale bundle | | @aircloudhq/web/model | modelName and the model naming helpers | | @aircloudhq/web/function | i18nFor, for functions |

Documentation

The full Rails-to-Air-Cloud map — every Action View helper and where it lives here — is the Rails to Air-Cloud reference in the Air-Cloud documentation. Its tests in a product run with @aircloudhq/testing. Questions and bug reports go to this package's issue tracker (the bugs link on its npm page).

License

MIT