Elements Installs From the npm Registry

Markdown

npm is three things under one name: a website for finding packages, a command line program, and a registry, which npm's docs describe as "a large public database of JavaScript software and the meta-information surrounding it" (About npm). The npm CLI is "the standard package manager for Node.js" (An introduction to the npm package manager). A project lists its dependencies in package.json, and npm install downloads them into a node_modules folder and records the exact versions in package-lock.json, a file "intended to be committed into source repositories" (npm install, package-lock.json). The same program runs project tasks with npm run, and runs each package's install scripts during an install unless you pass --ignore-scripts (npm scripts). The public registry is free to use, and private packages need a paid user or organization account (About private packages). npm is owned by GitHub, which agreed to acquire it in March 2020 (npm is joining GitHub), and GitHub is owned by Microsoft (Microsoft completes GitHub acquisition).

Elements works with one of those three parts and replaces another. Elements apps install packages from the npm registry: elements install stripe fetches the package from the registry and writes it into a real node_modules folder, and every npm package installs as is. What Elements replaces is the npm CLI. Elements is an integrated app environment for building web apps, built for people and their agents. At its center is a project server that runs while you work, and the same system comes with a package installer, a test runner, a bundled Postgres database, a deploy command for any Ubuntu server you reach over SSH, and a default framework that just works (start, packages).

The npm CLI installs packages and exits. What happens to your code next is up to the other tools in the project: a type checker, a test runner, a bundler and a migration tool, each started by its own line in the scripts block of package.json, each with its own configuration. Every seam between them is a place an app breaks, for a person or an agent, and an install changes code that each of those tools depends on. In Elements, the installer is a fully functional package installer and also part of the project server, so installs feed straight into the build: the next build checks your code against the packages that changed, runs the tests that depend on them, and reports every error from one command. The apps you build stay close to the metal (HTML, CSS, TypeScript, SQL and function calls), and the framework vocabulary is the bare minimum: app.route(), session.login(), sql(), @rpc and tests.

At a Glance

Elements npm CLI
What it is A package installer inside an integrated app environment, built around a project server that runs while you work A package manager and script runner
Where packages come from The npm registry, plus packages.elements.dev for the Elements packages (packages) The npm registry
Dependency list package.dependencies in config.jsoc, the same file that configures the app package.json
Lockfile package.lock, generated by the CLI and never edited by hand package-lock.json, with open issues about lost resolved and integrity fields
After an install The next build checks your code against the new packages and runs the affected tests No check of your code; the type check and tests are separate commands
A missing package A build error naming the package and the install command An error when the app or a tool loads the file
Running the project elements start, one project server for building, testing and running the app npm run scripts, one for each tool
Packages you are writing The version local, found on a search path and watched npm link symlinks, or workspaces under a root package.json
Installs on deploy Part of the build on each server, before the tests run and the release goes live npm ci in a pipeline, which deletes node_modules and installs again

Every Package on the npm Registry Installs in Elements

The npm registry holds most open-source JavaScript; in September 2022 it listed over 2.1 million packages (An introduction to the npm package manager). Elements apps install from that registry directly, with no separate list of approved packages.

In Elements, you add a package with the elements command, at its latest version, at a range or at a pinned version:

terminalElements
elements install dayjs # latest version elements install stripe@^14 # a range elements install dayjs@1.11.10 # a pin elements install -d @types/node # a dev dependency elements uninstall dayjs

The command writes the spec into package.dependencies in config.jsoc and the version it resolved to into package.lock, in one step. You can also edit the dependency map by hand and save, and the project server installs what changed (packages).

The installed tree is a real node_modules in Node's own layout: when two packages depend on different versions of one library, each gets its own copy, and tools that read node_modules find what they expect. Elements compiles every module to CommonJS on both the server and the browser, so packages published only as ES modules and packages published only as CommonJS both work in server code and in browser code (packages, build). The Elements packages, such as @elements/app and @elements/style, come from packages.elements.dev and install the same way.

The Next Build Checks Your Code Against the Install

npm install "installs a package and any packages that it depends on" (npm install), and that is where its job ends. It does not read your code. If an upgrade renames a function you call, or an uninstall removes a package you still import, you find out when you run the type check, the tests or the app.

In Elements, an install is work the project server does between builds, and the build that follows compiles your app against what was installed. Package code is built like your own: checked, tree-shaken and hot reloaded (packages). Your code is checked against each package's own types in that build, so a call the package does not support is a build error:

app/lib/dates.tsElements
import dayjs from "dayjs"; export function dueDate(days: number): string { return dayjs().add(days, "day").formatt("MMM D"); // error: Property 'formatt' does not exist on type 'Dayjs'. Did you mean 'format'? }

Uninstall a package that your code still imports, and the next build fails with "The dependency dayjs was not found. Is it installed?", along with the command that installs it. The build pins each package import to the file it found, and the app loads that same file when it runs (build). Tests whose dependencies changed run again in that build, and until every error is fixed, the running app keeps the last good release (tests).

Your terminal, your editor and several agents can all install in the same project. The project server queues each install between builds, so no two installs fight over node_modules and no build starts while an install is still writing (packages).

The Installer Owns the Lockfile

A lockfile is meant to pin every dependency to one version, so teammates, deployments and CI "install exactly the same dependencies" (package-lock.json). In npm, the lockfile and the version overrides around it have a record of their own in the issue tracker. As of October 3, 2026, the npm CLI repository has 632 open issues, 495 of them labeled Bug (open npm CLI issues, open Bug issues). Among the open issues with the most reactions:

In Elements, the CLI owns both files. package.lock is generated, and an existing resolution is reused from it, so a build stays on the versions it was tested against until you ask for a change. elements install -u moves each package to the newest version its spec allows, and -f resolves every dependency again. When a package cannot be resolved, the install stops with the reason and a non-zero exit, and leaves config.jsoc and package.lock as they were (packages, cli).

An install changes only what it must. The installer works out the new node_modules tree in memory, compares it with what is on disk, and writes the packages that differ; when nothing differs, it writes nothing. Deleting node_modules is safe, because the next install puts it back (packages).

One Project Server Instead of a Scripts Block

The npm CLI doubles as a task runner. The scripts block in package.json names commands that npm run <task-name> starts (An introduction to the npm package manager), and in a typical project those commands start a dev server, a type checker, a test runner and a migration tool. Each runs as its own process with its own output. An agent that changes a dependency has to run each one and piece the answers together before it knows what broke.

In Elements, those jobs belong to one build. elements start starts the project server, which runs while you work and holds the build graph and build state in memory. A build installs packages, compiles, migrates, tests and releases, and on every save the build state is updated almost instantly (build). elements build -json returns one structured answer with every build error, from type checking, tests, migrations and program analysis, and your terminal, your editor and every agent read the same build state.

An Agent Gets More From Correction Than From a Bigger Registry

Much of the case for the npm CLI is the registry behind it: a package for almost every job. More app code is now written by AI agents such as Claude Code, Codex and Cursor, and for an agent a package earns its place only when it does something the agent cannot write quickly from standards. Stripe's SDK or a date library can clear that bar. A package that pads a string does not: an agent writes those few lines of TypeScript faster than it can find, install and learn the package, and every dependency added past that point is another API to look up and another place for an upgrade to break the app.

Some framework belongs in every app. Between an agent writing machine code and a package for subtraction sits the plumbing every web app needs, and Elements ships it: routing with app.route(), typed server calls with @rpc, sessions with session.login(), and sql() with the database driver. Past that plumbing the returns diminish, so Elements keeps the rest to standards an agent already knows: pages in Elements HTML, which is standard HTML with TypeScript expressions in {}, styles in CSS, server code as TypeScript function calls, and queries in SQL.

Elements puts its engineering into correction instead: telling an agent what it got wrong the moment it gets it wrong. One coherent system checks every page, function, query, test and migration, including every line that calls into a package from the registry. The project server builds in milliseconds and answers agents and humans in microseconds, so an agent that adds a dependency learns what the new package broke from one command, before it moves on.

Packages You Are Writing Install From Their Folder

Many teams keep shared code in packages they have not published. The npm CLI offers two ways to use one in an app. npm link is two steps: run it in the package folder to create a global symlink, then run npm link <package> in the app to link it into node_modules, and the link is not saved to package.json by default (npm link). Workspaces automate the linking, for packages nested inside one root project with a workspaces field in its package.json (npm workspaces).

In Elements, give the dependency the version local, with elements install @acme/ui@local or by writing "local" in config.jsoc. The project server finds the package in the directories on ELEMENTS_PACKAGE_PATH, installs it, and watches it like your own code, so a save in the package rebuilds the app against it. Once the package is published, change local to a registry version and the next install replaces the local copy. Deploys carry local packages along and install them on each server (packages).

Installs on Deploy Run Inside the Build

In an npm project, the install on a deploy is usually npm ci, a clean install for automated environments. If node_modules is present, "it will be automatically removed before npm ci begins its install," and if the lockfile and package.json disagree, it exits with an error (npm ci). The pipeline then runs the build and the tests as further steps that you write and maintain.

elements deploy needs no pipeline. The project server on your machine sends only the changed files to the project server on each deploy machine, and each machine compiles, installs packages, runs the tests and switches to the new release in one step. An error in any phase stops the deploy, and the previous release keeps serving (what deploy does). The packages your app runs on in production are the packages the tests on that machine just ran against.

Who Runs npm

The npm registry and CLI are run by GitHub, which Microsoft owns (npm is joining GitHub, Microsoft completes GitHub acquisition). At the acquisition, GitHub wrote that the public registry "will always be available and always be free" (npm is joining GitHub), and npm charges for private packages through paid user and organization accounts (About private packages).

Elements keeps the free public registry and builds on it. Elements sells its tooling directly, per machine, with nothing metered (pricing). It charges for the tooling, not the framework: the app framework and runtime packages such as @elements/app are MIT licensed, and everything you build is yours (licenses).

Moving an npm Project to Elements

An agent can move an npm project to Elements easily: each entry under dependencies in package.json goes into config.jsoc, and elements install fetches those packages from the npm registry they came from. Every package the app depends on comes along, and from then on each install feeds one build, next to a default framework, bundled Postgres and a deploy command (packages, start).

Questions

Can an Elements app use packages from the npm registry?

Yes. The Elements installer resolves packages from the npm registry and writes a real node_modules folder, laid out the way Node resolves modules, and every npm package installs as is. Packages published only as ES modules and packages published only as CommonJS both work, in server code and in browser code, because Elements compiles every module to one format.

Do I need the npm CLI to build an Elements app?

No. The package installer is built into Elements and runs on the project server: elements install adds a package, elements uninstall removes one, and every build makes sure the packages your app declares are installed. There is no separate installer to run.

Does Elements use package.json and package-lock.json?

Elements declares dependencies under package.dependencies in config.jsoc, the same file that configures the app, and writes the resolved versions to package.lock, which the CLI generates. The elements install command updates both in one step.

Where do the @elements packages come from?

From packages.elements.dev. Every other package comes from the npm registry, and both install into the same node_modules folder.

What happens when an install breaks my code?

The next build reports it. When an install finishes, the project server builds the app against the new packages, so a call that no longer matches a package's types, a missing package or a failing test comes back as a build error from elements build -json, and a build with an error never reaches the running app.

How do I work on a package and the app that uses it at the same time?

Give the dependency the version local. Elements finds the package on the package search path, installs it, and watches it, so a save in the package rebuilds the app against it. There is no npm link step, and the projects do not have to share a repository.