Requirements
- Vue
^3.5.2as a peer dependency: Vue 3.5.2 or a later compatible Vue 3 release, not Vue 4. The declarations use Vue 3.5 types; Vue 3.0–3.4 are outside the supported peer range. - A Vue application build tool, such as Vite with its Vue plugin or Nuxt. The package ships compiled JavaScript and TypeScript declarations; consumers do not compile its source SFCs.
- A browser with CSS custom properties,
color-mix()and:has()for the full loader collection.
Install the package
Install the published MIT-licensed package using your preferred package manager. The documentation version follows the library source, not necessarily the currently published npm release. The playground's local monorepo dependency is only for maintaining this site; consumers do not need a repository clone, local build or link step.
npm install veloadRegister the stylesheet
Component styles are extracted into a single veload/style.css file. Import it once, globally. The JavaScript entry point does not inject styles automatically, so without this import the components render without their intended appearance and animations.
// nuxt.config.ts
export default defineNuxtConfig({
css: ["veload/style.css"],
});// main.ts
import { createApp } from "vue";
import "veload/style.css";
import App from "./App.vue";
createApp(App).mount("#app");Import the components
Every component and composable is a named export from veload. The root entry provides ESM, CommonJS and dist/index.d.ts declarations. Use the public package entry rather than importing private source or dist paths. There is no default plugin, Nuxt module or global loading manager. Import only what you use so the bundler can drop unused JavaScript; the stylesheet still includes every component.
import {
VLoading,
VLoadingButton,
VLoader,
VProgress,
VSkeleton,
VSkeletonSwitch,
VSpinner,
useDelayedLoading,
useLoading,
useLoadingGroup,
} from "veload";First use
In a Vue single-file component, <script setup> imports are available directly in the template; no registration step is needed. This example starts a request only on a button click and handles errors in the application. Supply your own /api/message endpoint returning a JSON string; the library does not provide it or perform requests automatically.
<script setup lang="ts">
import { ref } from "vue";
import { VLoading, useLoading } from "veload";
import "veload/style.css";
const { isLoading, execute } = useLoading();
const message = ref<string | null>(null);
async function load() {
try {
message.value = await execute(async () => {
const response = await fetch("/api/message");
if (!response.ok) throw new Error("Request failed");
return (await response.json()) as string;
});
} catch (error) {
message.value = error instanceof Error ? error.message : "Unknown error";
}
}
</script>
<template>
<VLoading :loading="isLoading" label="Loading message">
<button type="button" @click="load">Reload</button>
<p v-if="message">{{ message }}</p>
</VLoading>
</template>Optional registration
Prefer SFC imports. In an Options API component, use components: { VSpinner } after importing it. If your app intentionally uses global components, register individual named exports with Vue's app.component(), not app.use(Veload):
// main.ts (optional, instead of importing in each component)
import { createApp } from "vue";
import { VSpinner } from "veload";
import "veload/style.css";
import App from "./App.vue";
const app = createApp(App);
app.component("VSpinner", VSpinner);
app.mount("#app"); Runtime global registration does not add template types automatically. Prefer local imports for type inference, or declare Vue's GlobalComponents types in your app when using global components with a template type checker.
TypeScript
Prop types, loader variants and composable return types are exported from the package root. Declarations are included through the types export; no separate types package or shim is needed. Use import type for type-only imports and a Vue-aware checker such as vue-tsc (or Nuxt's nuxt typecheck) to check SFC templates.
import type {
LoaderVariant,
NamedLoaderProps,
VLoadingProps,
VProgressProps,
VSkeletonProps,
VSpinnerProps,
UseLoadingReturn,
} from "veload";Nuxt and server rendering
Register veload/style.css in the Nuxt css option above, then import components directly in page or component SFCs. The library can render on the server; no client-only plugin or ClientOnly wrapper is required.
For initial server-fetched data, use Nuxt's useFetch or useAsyncData so its payload is reused during hydration instead of issuing a second request. This example uses the same application-owned endpoint:
<script setup lang="ts">
import { VLoading } from "veload";
const { data: message, status, error, refresh } =
await useFetch<string>("/api/message");
</script>
<template>
<VLoading :loading="status === 'pending'" label="Loading message">
<button type="button" @click="refresh()">Reload</button>
<p v-if="error">Could not load the message.</p>
<p v-else-if="message">{{ message }}</p>
</VLoading>
</template> Create useLoading() and useDelayedLoading() inside component setup, not at module scope where mutable state could be shared between SSR requests. Use them for user-triggered work; they track activity, not Nuxt data fetching or payload serialization. Keep browser-only work in event handlers or onMounted, and use the same initial loading state on the server and client to avoid hydration mismatches.