Vue 3 composables and components for the UniRate currency-exchange API.
useExchangeRate(from, to)— single rateuseRates(from)— full rate table for a baseuseConvert(from, to, amount)— converted amountuseCurrencies()— list of supported codes (~600)useHistoricalRate(date, from, to)— historical rate (Pro)<Currency :amount from to />— drop-in€92.50<Rate from to />— drop-in1.0823createUniRate()— one client for the whole app- Zero runtime deps. Native
fetch. AbortController cleanup on unmount.
Every composable argument may be a plain value, a ref, or a getter — the
request re-runs reactively when any of them change.
npm install @unirate/vue
# or
pnpm add @unirate/vue
# or
yarn add @unirate/vuePeer dep: vue ^3.3 (needs toValue, shipped in 3.3+). Requires Node 18.17+
for build/server use.
Install the plugin once at the app root:
// main.ts
import { createApp } from "vue";
import { createUniRate } from "@unirate/vue";
import App from "./App.vue";
createApp(App)
.use(createUniRate({ apiKey: import.meta.env.VITE_UNIRATE_API_KEY }))
.mount("#app");Then use composables and components anywhere in the tree:
<script setup lang="ts">
import { useExchangeRate, Currency } from "@unirate/vue";
const { data: rate, isLoading, error } = useExchangeRate("USD", "EUR");
</script>
<template>
<p v-if="isLoading">Loading…</p>
<p v-else-if="error">Couldn't load the rate.</p>
<p v-else>
1 USD = {{ rate }} EUR. Your total:
<Currency :amount="42.99" from="USD" to="EUR" />
</p>
</template>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/vueruns 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 UniRate package that proxies it (@unirate/nuxt,@unirate/next,@unirate/sveltekit) in front, or pass a custombaseUrlpointing at your own proxy.
Every composable returns the same shape — refs, so your template re-renders when the request settles:
interface QueryState<T> {
data: Ref<T | undefined>; // settled value, undefined until first success
error: Ref<Error | undefined>; // last error, undefined while healthy
isLoading: Ref<boolean>; // true until the first settle (first-paint skeletons)
isFetching: Ref<boolean>; // true whenever a request is in flight, incl. refetch
refetch: () => void; // imperatively re-run
}const { data: rate } = useExchangeRate("EUR", "USD");
// rate.value === 1.0823const { data: rates } = useRates("USD");
// rates.value === { EUR: 0.92, GBP: 0.80, JPY: 149.3, ... }const amount = ref(100);
const { data: converted } = useConvert("USD", "EUR", amount);
// converted.value === 92.5 — recomputes when amount.value changesconst { data: codes } = useCurrencies();
// codes.value === ["USD", "EUR", "GBP", ...] (~600 incl. crypto)const { data, error } = useHistoricalRate("2024-01-01", "USD", "EUR");
// On the free tier this resolves error.value to a ProRequiredError.Arguments accept refs or getters, and options.enabled (also reactive) defers
the request until dependent state is ready:
const base = ref("USD");
const ready = ref(false);
const { data } = useRates(base, { enabled: ready });
// fires once ready.value flips true, and refires whenever base.value changesoptions.client overrides the injected client for a single call (tests, or a
second UniRate account/proxy).
<template>
<!-- "€92.50" once the rate arrives -->
<Currency :amount="100" from="USD" to="EUR" />
<!-- "1.0823" -->
<Rate from="EUR" to="USD" />
</template>Both accept :decimals and :locale. <Currency> defaults from to "USD".
Customize the loading and error states with slots:
<Currency :amount="100" from="USD" to="EUR">
<template #loading><Spinner /></template>
<template #error="{ error }">—</template>
</Currency>When <Currency> errors and no #error slot is given, it falls back to the
unconverted amount in from so layouts don't collapse. <Rate> falls back to
the loading placeholder.
Need a rate outside a component (a Pinia action, a route guard)? Import the zero-dep client directly:
import { UniRateClient } from "@unirate/vue/client";
const client = new UniRateClient({ apiKey: process.env.UNIRATE_API_KEY! });
const rate = await client.getRate("USD", "EUR"); // 0.92The client maps HTTP status codes to typed errors, all extending UniRateError:
| 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;
vueis a peer provided by your app. - Native
fetchonly — no transitive HTTP-client supply-chain surface. - In-flight requests are aborted via
AbortControlleron unmount. - Published to npm with provenance attestation.
Official UniRate clients & framework integrations: Python · Node · React · Vue · Nuxt · Next.js · SvelteKit · NestJS · Astro · Eleventy · MCP server
UniRate ships official integrations for 40+ ecosystems, all maintained under the UniRate-API org.
Core clients (9 languages) Python · Node.js / TypeScript · Go · Rust · Java · Ruby · PHP · .NET · Swift
JavaScript / TypeScript React · Next.js · Remix · SvelteKit · Vue · Angular · Nuxt · NestJS · tRPC
Static-site generators Astro · Eleventy · Hugo · Jekyll
CMS & e-commerce Wagtail · WordPress · WooCommerce · Drupal · Strapi · Medusa · Symfony · Laravel · Directus
Data, AI & backend LangChain (Python) · LangChain.js · FastAPI · Flask · Django REST Framework · Apache Airflow · dbt
Platform & tools MCP server · CLI · Cloudflare Workers · Home Assistant · n8n · Google Sheets · VS Code · Obsidian
Money library bridges money gem (Ruby) · NodaMoney (.NET)
Get a free API key at unirateapi.com.
MIT © UniRate