SolidJS primitives and a provider for the UniRate currency-exchange API. Every primitive returns a Solid Resource, so loading / error / refetch come for free and the request re-runs reactively when its inputs change.
createRate(from, to)— single rate →Resource<number>createRates(from)— full rate table for a base →Resource<Record<string, number>>createConversion(from, to, amount)— converted amount →Resource<number>createCurrencies()— supported codes (~600) →Resource<string[]>createVatRates()/createVatRates(country)— VAT rates →Resource<VATRatesAll | VATRateOne>createHistoricalRate(date, from, to)— historical rate (Pro) →Resource<number><UniRateProvider apiKey="...">— one client for the whole appuseUniRate()— grab the client imperatively- Zero runtime deps. Native
fetch. Requests aborted on cleanup.
Every argument may be a plain value or a Solid accessor (a signal getter) — pass an accessor and the resource re-fetches reactively when it changes.
npm install @unirate/solid
# or
pnpm add @unirate/solid
# or
yarn add @unirate/solidPeer dep: solid-js >=1.8. Requires Node 18.17+ for build/server use.
Wrap your app once in <UniRateProvider>:
import { render } from "solid-js/web";
import { UniRateProvider } from "@unirate/solid";
import App from "./App";
render(
() => (
<UniRateProvider apiKey={import.meta.env.VITE_UNIRATE_API_KEY}>
<App />
</UniRateProvider>
),
document.getElementById("root")!,
);Then reach for a primitive anywhere in the tree:
import { Show } from "solid-js";
import { createRate } from "@unirate/solid";
function Ticker() {
const [rate] = createRate("USD", "EUR");
return (
<Show when={!rate.loading} fallback={<p>Loading…</p>}>
<Show when={!rate.error} fallback={<p>Couldn't load the rate.</p>}>
<p>1 USD = {rate()} EUR</p>
</Show>
</Show>
);
}Get a free API key at unirateapi.com — the free tier covers latest rates and conversions for ~600 currencies including crypto. Historical rates, time-series, and commodity feeds require Pro.
Where does the key live?
@unirate/solidruns in the browser, so the API key ships to the client. UniRate keys are scoped to the free/Pro tier you choose; if you need the key kept server-side, put a proxy in front (a SolidStart server function, or pass a custombaseUrlpointing at your own proxy).
Each primitive returns Solid's standard [Resource, { refetch, mutate }] tuple:
const [rate, { refetch }] = createRate("USD", "EUR");
rate(); // the settled value, or undefined until first success
rate.loading; // true while a request is in flight
rate.error; // the last error, or undefined while healthy
rate.state; // "unresolved" | "pending" | "ready" | "refreshing" | "errored"
refetch(); // imperatively re-runconst [rate] = createRate("EUR", "USD");
// rate() === 1.0823const [rates] = createRates("USD");
// rates() === { EUR: 0.92, GBP: 0.80, JPY: 149.3, ... }const [amount, setAmount] = createSignal(100);
const [converted] = createConversion("USD", "EUR", amount);
// converted() === 92.5 — recomputes when amount() changesconst [codes] = createCurrencies();
// codes() === ["USD", "EUR", "GBP", ...] (~600 incl. crypto)const [all] = createVatRates(); // Resource<VATRatesAll>
const [de] = createVatRates("DE"); // Resource<VATRateOne>
// de()?.vat_data.vat_rate === 19const [hist] = createHistoricalRate("2024-01-01", "USD", "EUR");
// On the free tier hist.error is a ProRequiredError.Any argument accepts a plain value or an accessor, and options.enabled (also
value-or-accessor) defers the request until dependent state is ready:
const [base, setBase] = createSignal("USD");
const [ready, setReady] = createSignal(false);
const [rates] = createRates(base, { enabled: ready });
// fires once ready() flips true, and refires whenever base() changesoptions.client overrides the context client for a single call (tests, or a
second UniRate account/proxy).
Need a rate outside a component (a route data function, a server function)? Import the zero-dep client directly:
import { UniRateClient } from "@unirate/solid/client";
const client = new UniRateClient({ apiKey: process.env.UNIRATE_API_KEY! });
const rate = await client.getRate("USD", "EUR"); // 0.92Inside a component you can also grab the context client with useUniRate().
The client maps HTTP status codes to typed errors, all extending UniRateError.
They surface through each resource's .error:
| Status | Error | Meaning |
|---|---|---|
| 400 | InvalidRequestError |
Bad parameters |
| 401 | AuthenticationError |
Missing/invalid API key |
| 403 | ProRequiredError |
Endpoint requires Pro |
| 404 | InvalidCurrencyError |
Unknown currency / no data |
| 429 | RateLimitError |
Rate limit exceeded |
- Zero runtime dependencies;
solid-jsis a peer provided by your app. - Native
fetchonly — no transitive HTTP-client supply-chain surface. - In-flight requests are aborted on cleanup via
AbortController. - Published to npm with provenance attestation.
Official UniRate clients & framework integrations: Python · Node · React · Vue · Angular · Nuxt · Next.js · SvelteKit · Astro · MCP server
MIT © Unirate Team