Vue
Server-render Vue 3 components with Composition API and full TypeScript support.
Build Configuration
Add Vue to your build by specifying the directory containing your Vue components:
1const manifest = await build({
2 vueDirectory: 'frontend/vue'
3});Page Handler
Use handleVuePageRequest with asset() to get the compiled paths for both the page and index files. The fourth parameter is a head tag string generated by generateHeadElement:
1// backend/server.ts
2import { handleVuePageRequest, generateHeadElement, asset } from '@absolutejs/absolute';
3
4new Elysia()
5 .get('/products/:id', async ({ params }) => {
6 const product = await getProduct(params.id);
7 const relatedProducts = await getRelatedProducts(product.categoryId);
8
9 return handleVuePageRequest(
10 asset(manifest, 'Products'), // Compiled page for SSR
11 asset(manifest, 'ProductsIndex'), // Compiled index for hydration
12 generateHeadElement({ // Head tags
13 title: `${product.name} | My Store`,
14 meta: [
15 { name: 'description', content: product.description }
16 ]
17 }),
18 { product, relatedProducts } // Type-safe props
19 );
20 })Generate Head
generateHeadElement creates the head tag string from a structured object. This provides a type-safe way to set titles, meta tags, and link elements:
1import { generateHeadElement } from '@absolutejs/absolute';
2
3// generateHeadElement creates the head tag string for you
4const head = generateHeadElement({
5 title: 'Page Title',
6 meta: [
7 { name: 'description', content: 'Page description' },
8 { name: 'keywords', content: 'vue, ssr, absolutejs' },
9 { property: 'og:title', content: 'Open Graph Title' },
10 { property: 'og:image', content: '/images/og.png' }
11 ],
12 link: [
13 { rel: 'canonical', href: 'https://example.com/page' },
14 { rel: 'icon', href: '/favicon.ico' }
15 ]
16});
17
18// Returns a string like:
19// <title>Page Title</title>
20// <meta name="description" content="Page description">
21// <meta property="og:title" content="Open Graph Title">
22// <link rel="canonical" href="https://example.com/page">- title: Sets the page title
- meta: Array of meta tags with name/property and content
- link: Array of link elements for canonical URLs, icons, etc.
Components
Use Vue 3 Single File Components with the Composition API. defineProps provides type-safe prop access:
1<!-- src/vue/pages/Products.vue -->
2<script setup lang="ts">
3type Product = {
4 id: string;
5 name: string;
6 price: number;
7 description: string;
8};
9
10type ProductsProps = {
11 product: Product;
12 relatedProducts: Product[];
13};
14
15const props = defineProps<ProductsProps>();
16</script>
17
18<template>
19 <div class="product-page">
20 <h1>{{ props.product.name }}</h1>
21 <p class="price">${{ props.product.price }}</p>
22 <p>{{ props.product.description }}</p>
23
24 <section v-if="props.relatedProducts.length > 0">
25 <h2>Related Products</h2>
26 <ul>
27 <li v-for="related in props.relatedProducts" :key="related.id">
28 <a :href="`/products/${related.id}`">{{ related.name }}</a>
29 </li>
30 </ul>
31 </section>
32 </div>
33</template>
34
35<style scoped>
36.price {
37 font-size: 1.5rem;
38 color: var(--primary);
39}
40</style>Type Safety
Vue 3's defineProps with TypeScript generics provides complete compile-time type checking. Errors are caught before your code runs.
1// Vue 3 with TypeScript provides complete type safety
2// defineProps<T>() ensures compile-time type checking
3
4<script setup lang="ts">
5// Types are enforced at compile time
6type User = {
7 id: string;
8 name: string;
9 role: 'admin' | 'user';
10};
11
12type AdminDashboardProps = {
13 user: User;
14 systemStats: SystemStats;
15};
16
17// TypeScript error if server sends wrong types!
18const props = defineProps<AdminDashboardProps>();
19
20// Computed properties are also type-safe
21const isAdmin = computed(() => props.user.role === 'admin');
22</script>- Server to client: Props types are validated end-to-end
- Computed properties: Derived values maintain type safety
- Template type checking: Vue Language Server validates template bindings
Vue Imports
When using Vue and Svelte in the same project, TypeScript may have conflicts between .vue and .svelte file type definitions. Import all Vue components in a seperate file and export that object to avoid these conflicts.
1import SvelteExample from './svelte/pages/SvelteExample.svelte';
2import { vueImports } from './vueImporter';
3
4const manifest = await build({
5 vueDirectory: 'frontend/vue',
6 svelteDirectory: 'frontend/svelte',
7});
8
9export const server = new Elysia()
10 .get('/svelte', async () =>
11 handleSveltePageRequest(
12 SvelteExample,
13 asset(manifest, 'SvelteExample'),
14 asset(manifest, 'SvelteExampleIndex'),
15 {
16 cssPath: asset(manifest, 'SvelteExampleCSS'),
17 initialCount: 0
18 }
19 )
20 )
21 .get('/vue', () =>
22 handleVuePageRequest(
23 vueImports.VueExample,
24 asset(manifest, 'VueExample'),
25 asset(manifest, 'VueExampleIndex'),
26 generateHeadElement({
27 cssPath: asset(manifest, 'VueExampleCSS'),
28 title: 'AbsoluteJS + Vue'
29 }),
30 { initialCount: 0 }
31 )
32 )
33;This is only required when both Vue and Svelte are used in the same project. If you're only using Vue, you can omit this option.