Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/meteor/meteor/llms.txt

Use this file to discover all available pages before exploring further.

Vue is a progressive framework for building user interfaces. Meteor provides excellent integration with Vue through specialized packages, combining Vue’s reactive components with Meteor’s real-time data layer.

Installation

Create a new Vue app with Meteor:
meteor create my-app --vue
Or add Vue to an existing project:
meteor npm install --save vue
For Single File Component (.vue) support:
meteor add akryum:vue-component

Project Structure

A typical Vue+Meteor app consists of:
/client
  main.html        # HTML with #app div
  main.js          # Vue app initialization
  App.vue          # Root component
/imports
  /api             # Collections and methods
  /ui              # Vue components
/server
  main.js          # Server startup

Basic Setup

HTML Template

Create /client/main.html with the app container:
<body>
  <div id="app"></div>
</body>

Client Entry Point

Initialize Vue in /client/main.js:
import { createApp } from 'vue';
import App from './App.vue';
import './main.html';

Meteor.startup(() => {
  const app = createApp(App);
  app.mount('#app');
});

Root Component

Create /client/App.vue:
<template>
  <div>
    <p>This is a Vue component and below is the current date:<br />{{ date }}</p>
  </div>
</template>

<script>
export default {
  data() {
    return {
      date: new Date(),
    };
  }
}
</script>

<style scoped>
p {
  font-size: 2em;
  text-align: center;
}
</style>

Reactive Data Integration

Integrate Meteor’s reactive data system with Vue using vue-meteor-tracker:
meteor npm install --save vue-meteor-tracker

Plugin Setup

Register the plugin in /client/main.js:
import { createApp } from 'vue';
import VueMeteorTracker from 'vue-meteor-tracker';
import App from './App.vue';
import './main.html';

const app = createApp(App);
app.use(VueMeteorTracker);

Meteor.startup(() => {
  app.mount('#app');
});

Using Meteor Reactivity

The meteor option in Vue components provides reactive Meteor data:
<template>
  <div>
    <div v-if="!$subReady.Time">Loading...</div>
    <div v-else>
      <p>Hello {{ hello }},<br>The time is now: {{ currentTime }}</p>
      <button @click="updateTime">Update Time</button>
      
      <p>Startup times:</p>
      <ul>
        <li v-for="t in TimeCursor" :key="t._id">
          {{ t.time }} - {{ t._id }}
        </li>
      </ul>
    </div>
  </div>
</template>

<script>
import '/imports/collections/Time';

export default {
  data() {
    return {
      hello: 'World',
      settings: Meteor.settings.public,
    };
  },
  
  methods: {
    updateTime() {
      Meteor.call('UpdateTime');
    }
  },
  
  // Meteor reactivity
  meteor: {
    // Subscriptions
    $subscribe: {
      'Time': []
    },
    
    // Reactive helper - returns current time
    currentTime() {
      const t = Time.findOne('currentTime') || {};
      return t.time;
    },
    
    // Reactive cursor
    TimeCursor() {
      return Time.find({}, {
        sort: { time: -1 }
      });
    },
  }
}
</script>

Collections and Publications

Define a collection in /imports/collections/Time.js:
import { Mongo } from 'meteor/mongo';

export const Time = new Mongo.Collection('time');

Router Integration

Vue Router works seamlessly with Meteor:
meteor npm install vue-router

Router Configuration

import { createRouter, createWebHistory } from 'vue-router';
import Home from './pages/Home.vue';
import Tasks from './pages/Tasks.vue';

const routes = [
  { path: '/', component: Home },
  { path: '/tasks', component: Tasks },
];

const router = createRouter({
  history: createWebHistory(),
  routes,
});

export default router;

Using the Router

import { createApp } from 'vue';
import VueMeteorTracker from 'vue-meteor-tracker';
import router from './router';
import App from './App.vue';

const app = createApp(App);
app.use(VueMeteorTracker);
app.use(router);

Meteor.startup(() => {
  app.mount('#app');
});
In your root component:
<template>
  <div class="p-8">
    <AppMenu />
    <router-view />
  </div>
</template>

<script setup>
import AppMenu from './components/AppMenu.vue';
</script>

Server-Side Rendering (SSR)

Meteor supports Vue SSR using the akryum:vue-ssr package:
meteor add akryum:vue-ssr
meteor npm install --save vue-server-renderer

Basic SSR Setup

In /server/main.js:
import { VueSSR } from 'meteor/akryum:vue-ssr';
import createApp from './app';

VueSSR.createApp = function () {
  const { app } = createApp();
  return { app };
};

SSR with Routing

import { VueSSR } from 'meteor/akryum:vue-ssr';
import createApp from './app';

VueSSR.createApp = function (context) {
  const { app, router } = createApp();
  
  // Set router location from context
  router.push(context.url);
  
  return { app };
};

Async Data Hydration

For components with async data requirements:
VueSSR.createApp = function (context) {
  return new Promise((resolve, reject) => {
    const { app, router, store } = createApp({ ssr: true });
    
    router.push(context.url);
    
    router.onReady(async () => {
      const matchedComponents = router.getMatchedComponents();
      
      if (!matchedComponents.length) {
        reject(new Error('not-found'));
      }
      
      // Get components with asyncData
      const componentsWithAsyncData = matchedComponents
        .filter(component => component.asyncData);
      
      // Call asyncData methods
      const asyncDataPromises = componentsWithAsyncData.map(component => (
        component.asyncData({ store, route: router.currentRoute })
      ));
      
      // Wait for all async data
      await Promise.all(asyncDataPromises);
      
      // Serialize state for client hydration
      const js = `window.__INITIAL_STATE__=${JSON.stringify(store.state)};`;
      
      resolve({ app, js });
    });
  });
};

Client Hydration

In /client/main.js:
import { createApp } from 'vue';
import createAppInstance from './app';

Meteor.startup(() => {
  const { app, store } = createAppInstance({ ssr: false });
  
  // Hydrate Vuex store with SSR data
  if (window.__INITIAL_STATE__) {
    store.replaceState(window.__INITIAL_STATE__);
  }
  
  app.mount('#app');
});

Composition API

Vue 3 Composition API works perfectly with Meteor:
<template>
  <div>
    <h2>Learn Meteor!</h2>
    <ul>
      <li v-for="link in links" :key="link._id">
        <a :href="link.url" target="_blank">{{ link.title }}</a>
      </li>
    </ul>
  </div>
</template>

<script setup>
import { ref } from 'vue';
import { useTracker } from 'vue-meteor-tracker';
import { LinksCollection } from '../api/links';

const links = useTracker(() => {
  Meteor.subscribe('links');
  return LinksCollection.find({}).fetch();
});
</script>

Best Practices

Always remove the autopublish package in production:
meteor remove autopublish
This ensures you explicitly control what data is published to clients.
Use <style scoped> in .vue files to prevent style leakage:
<style scoped>
p {
  font-size: 2em;
}
</style>
Return cursors directly from meteor helpers for automatic reactivity:
meteor: {
  tasks() {
    return Tasks.find({}); // Returns cursor, not array
  }
}
Use $subReady to show loading states:
<div v-if="!$subReady.tasks">Loading...</div>

Style Guide

Follow both guides for best results:

Meteor Style Guide

Meteor’s code style and structure recommendations

Vue Style Guide

Vue’s official style guide and best practices

Resources

Vue Tutorial

Step-by-step tutorial for building Vue apps with Meteor

Vue Meteor Tracker

Integration package for Vue and Meteor reactivity

Vue Documentation

Official Vue 3 documentation and guides

Vue SSR Guide

Server-side rendering with Vue