Installation Guide β
Tempo is designed to be environment-agnostic. Whether you are building a server-side application, a modern browser project with ESM, or a performance-critical "Lite" bundle, Tempo provides a specific path for you.
Temporal Polyfill Note β
Tempo expects the host environment to provide Temporal, either through native runtime support or a user-supplied polyfill.
Temporal has reached Stage 4 of the TC39 standards process (the committee that evolves JavaScript) and is shipping natively in modern browser environments (Deno 2.7+, Chrome 144+, Firefox 139+). For Node.js, native unflagged support is planned for a future release, but it can currently be tested via the --harmony-temporal flag. You can verify current browser support at caniuse.com/temporal. To avoid needlessly inflating package sizes for modern apps, Tempo does not bundle a Temporal polyfill by default.
WARNING
Node.js environments that ship Temporal behind a feature flag (--harmony-temporal) may have incomplete implementations. For stability, we strongly recommend using @js-temporal/polyfill instead of the native flag until you upgrade to an official unflagged release.
You can check at runtime with a simple guard:
if (typeof globalThis.Temporal === 'undefined') {
// Load your Temporal polyfill for this environment
}Note: The examples below include a polyfill for demonstration purposes only, so the snippets work consistently across environments.
π» Server & Bundlers (Node.js, Bun, Vite) β
For most modern projects using a package manager, install Tempo via the npm registry.
npm install @magmacomputing/tempo # npm
yarn add @magmacomputing/tempo # yarn
pnpm add @magmacomputing/tempo # pnpm
bun add @magmacomputing/tempo # bunUsage β
import { Tempo } from '@magmacomputing/tempo';
const t = new Tempo('next Friday');Node.js (with Native Temporal) β
When native unflagged Temporal support is officially released in Node.js, it will be fully supported and enabled by default.
For current Node.js releases that still ship Temporal behind a flag, you can enable it with:
node --harmony-temporal my-app.js(Note: See the Temporal Polyfill Note for warnings about using this flag in production)
Node.js (with Polyfill) β
The polyfill import shown here is conditional guidance, not required for all environments.
npm install @js-temporal/polyfillimport '@js-temporal/polyfill';
import { Tempo } from '@magmacomputing/tempo';
const t = new Tempo('next Friday');π¦ Deno β
Tempo is a native ESM package and works perfectly with Deno. You can add it via the deno add command which will resolve it from the npm registry.
As of Deno 2.7, the Temporal API is fully stabilized and enabled by default. You no longer need to pass the --unstable-temporal flag to use it.
deno add npm:@magmacomputing/tempoUsage β
import { Tempo } from "@magmacomputing/tempo";
const t = new Tempo();π Browser & Native Environments β
Tempo provides multiple native browser distribution formats. Here is the quick breakdown of which approach to use:
- Standard Usage (No plugins): Use the Native ESM Bundle.
- Plugins without a bundler: Use Smart CDNs (Easiest setup) OR Static CDNs (Best production performance).
- Plugins with a bundler (Vite/Webpack): Do nothing. Your bundler handles the resolution automatically.
- Non-ESM Environments: Use the UMD Global Variable approach.
1. The Global Bundle (Standard Usage) β
The easiest way to use Tempo natively in the browser is via the pre-optimized ESM bundle. It includes the entire core engine in a single file, eliminating network waterfall effects.
<script type="importmap">
{
"imports": {
"@js-temporal/polyfill": "https://cdn.jsdelivr.net/npm/@js-temporal/polyfill@0.5.1/dist/index.esm.js",
"@magmacomputing/tempo": "https://cdn.jsdelivr.net/npm/@magmacomputing/tempo@3/dist/tempo.bundle.esm.js"
}
}
</script><script type="module">
import '@js-temporal/polyfill';
import { Tempo } from '@magmacomputing/tempo';
const t = new Tempo('tomorrow');
console.log(t.format('{mon} {dd:ord}'));
</script>2. Smart CDNs (The "Best-of-Both-Worlds") β
If you want the absolute easiest setup for Tempo Premium Plugins natively in the browser, use an on-the-fly bundling CDN like esm.sh. Smart CDNs act like a Node environmentβthey read the package resolution rules and resolve nested dependencies automatically, meaning you don't have to map any internal subpaths.
While you could import directly from the URL everywhere, the best practice is to use a tiny import map for your top-level packages to keep your application code clean:
<!-- 1. A tiny import map for your clean shortcuts -->
<script type="importmap">
{
"imports": {
"@js-temporal/polyfill": "https://esm.sh/@js-temporal/polyfill@0.5.1",
"@magmacomputing/tempo": "https://esm.sh/@magmacomputing/tempo@3",
"@magmacomputing/tempo-plugin-ticker": "https://esm.sh/@magmacomputing/tempo-plugin-ticker@2"
}
}
</script>
<!-- 2. Your Application Code -->
<script type="module">
// You can use standard bare specifiers thanks to the tiny import map above.
// esm.sh handles all the complex internal plugin routing behind the scenes!
import '@js-temporal/polyfill';
import { Tempo } from '@magmacomputing/tempo';
import { TickerModule } from '@magmacomputing/tempo-plugin-ticker';
Tempo.extend(TickerModule);
</script>β οΈ Trade-offs of using Smart CDNs in Production
While esm.sh is fantastic for prototyping and reducing import map complexity, there are architectural trade-offs to consider before using it in a mission-critical production environment:
- Network Waterfalls: The browser must fetch the module, parse it, and then fetch its nested dependencies sequentially. This can slow down page load times compared to a fully bundled application.
- Uptime Dependency: You are introducing a critical third-party dependency into your runtime. If the CDN experiences routing issues, your application could break for end-users.
- Sub-dependency Version Floating:
esm.shautomatically resolves sub-dependencies based on semver constraints. If a sub-dependency introduces an accidental breaking change, it could affect your app. - Suboptimal Tree Shaking: The browser will download the entire module graph for that package; you cannot easily tree-shake unused exports as you can with a dedicated bundler like Vite or Webpack.
- Environment Parity: Handling development versus production environments (like
process.env.NODE_ENV) requires query parameters (e.g.,?dev), which complicates deployment.
3. Static CDNs (Production-Ready) β
For production environments where uptime and load speeds are critical, you should use a static file CDN (like jsdelivr). Because static CDNs serve raw files without compiling them on the fly, they are significantly faster and more reliable than Smart CDNs.
To use Tempo Premium Plugins via a static CDN, you simply need to explicitly map the unified plugin-api subpath so the browser knows how to resolve the internal connections:
<script type="importmap">
{
"imports": {
"@magmacomputing/tempo": "https://cdn.jsdelivr.net/npm/@magmacomputing/tempo@3/dist/tempo.index.js",
"@magmacomputing/tempo/plugin-api": "https://cdn.jsdelivr.net/npm/@magmacomputing/tempo@3/dist/plugin-api.index.js",
"@magmacomputing/tempo-plugin-astro": "https://cdn.jsdelivr.net/npm/@magmacomputing/tempo-plugin-astro@2/dist/index.js",
"@magmacomputing/tempo-plugin-ticker": "https://cdn.jsdelivr.net/npm/@magmacomputing/tempo-plugin-ticker@2/dist/index.js"
}
}
</script>Cache Busting
The jsdelivr CDN aggressively caches major version tags (like @3). When relying on precise module resolution for plugins, it is highly recommended to use explicit patch versions (like @3.0.1) to avoid fetching mismatched or outdated sub-modules.
π¦ Browser (UMD / Global Variable) β
If you aren't using ESM or just want a simple <script> tag for rapid prototyping, use the UMD global bundle. This attaches Tempo to the window object.
<!-- Load the Temporal Polyfill first -->
<script src="https://cdn.jsdelivr.net/npm/@js-temporal/polyfill@0.5.1/dist/index.umd.js"></script>
<!-- Load the Tempo Global Bundle -->
<script src="https://cdn.jsdelivr.net/npm/@magmacomputing/tempo@3/dist/tempo.bundle.js"></script>
<script>
const t = new Tempo('now');
console.log(t.toString());
</script>π§ͺ Granular "Lite" Builds (Advanced) β
If you are extremely concerned about bundle size, you can bypass the "Batteries Included" entry point and import only the core engine. You then manually opt-in to the modules you need.
import { Tempo } from '@magmacomputing/tempo/core';
import { MutateModule } from '@magmacomputing/tempo/mutate';
// Opt-in to specific functionality
Tempo.extend(MutateModule);
const t = new Tempo().add({ days: 1 });WARNING
When using the Lite build, the Tempo class will have almost no methods (like .add(), .set(), or .format()) until you explicitly call Tempo.extend() with the appropriate module.
π‘οΈ Versioning Policy β
We recommend pinning your versions in production environments to ensure stability.
- JSDelivr:
https://cdn.jsdelivr.net/npm/@magmacomputing/tempo@3/...(Locks to major version 3) - Latest:
https://cdn.jsdelivr.net/npm/@magmacomputing/tempo/...(Omit the version string to always receive the latest release. Note that JSDelivr will resolve a missing version tag to the latest published release).