AbsoluteJS
AbsoluteJS

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:

TS
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:

TS
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:

TS
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:

VUE
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.

VUE
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.

TS
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.