VueJS demo todo app guide

This guide provides step-by-step instructions for setting up and running a Jazz-powered Todo application using VueJS.

See the full example here.


Setup

Create a new app

Run the following command to create a new VueJS application:

 pnpm create vue@latest

 Project name: vue-setup-guide
 Add TypeScript? Yes
 Add JSX Support? No
 Add Vue Router for Single Page Application development? Yes
 Add Pinia for state management? No
 Add Vitest for Unit Testing? No
 Add an End-to-End Testing Solution? No
 Add ESLint for code quality? Yes
 Add Prettier for code formatting? Yes

Install dependencies

Run the following command to install Jazz libraries:

pnpm install jazz-tools jazz-browser jazz-vue

Implement schema.ts

Define the schema for your application.

Example schema inside src/schema.ts for a todo app:

import { Account, CoList, CoMap, Group, Profile, co } from "jazz-tools";

export class ToDoItem extends CoMap {
  name = co.string;
  completed = co.boolean;
}

export class ToDoList extends CoList.Of(co.ref(ToDoItem)) {}

export class Folder extends CoMap {
  name = co.string;
  items = co.ref(ToDoList);
}

export class FolderList extends CoList.Of(co.ref(Folder)) {}

export class ToDoAccountRoot extends CoMap {
  folders = co.ref(FolderList);
}

export class ToDoAccount extends Account {
  profile = co.ref(Profile);
  root = co.ref(ToDoAccountRoot);

  migrate() {
    if (!this._refs.root) {
      const group = Group.create({ owner: this });
      const firstFolder = Folder.create(
        {
          name: "Default",
          items: ToDoList.create([], { owner: group }),
        },
        { owner: group },
      );


      this.root = ToDoAccountRoot.create(
        {
          folders: FolderList.create([firstFolder], {
            owner: this,
          }),
        },
        { owner: this },
      );
    }
  }
}

Refactor main.ts

Update the src/main.ts file to integrate Jazz:

import "./assets/main.css";
import { JazzProvider } from "jazz-vue";
import { createApp, defineComponent, h } from "vue";
import App from "./App.vue";
import router from "./router";
import { ToDoAccount } from "./schema";

declare module "jazz-vue" {
  interface Register {
    Account: ToDoAccount;
  }
}

const RootComponent = defineComponent({
  name: "RootComponent",
  setup() {
    return () => [
      h(
        JazzProvider,
        {
          AccountSchema: ToDoAccount,
          auth: authMethod.value,
          peer: "wss://cloud.jazz.tools/?key=vue-todo-example-jazz@garden.co",
        },
        {
          default: () => h(App),
        },
      ),
    ];
  },
});

const app = createApp(RootComponent);

app.use(router);

app.mount("#app");

Set up router/index.ts:

Create a basic Vue router configuration. For example:

import { createRouter, createWebHistory } from "vue-router";
import HomeView from "../views/HomeView.vue";

const router = createRouter({
  history: createWebHistory(import.meta.env.BASE_URL),
  routes: [
    {
      path: "/",
      name: "Home",
      component: HomeView,
    },
  ],
});

export default router;

Implement App.vue

Update the App.vue file to include logout functionality:

<template>
  <div class="app-container">
    <header v-if="me" class="app-header">
      <h1>Todo App</h1>
      <div class="user-section">
        <span>{{ me.profile?.name }}</span>
        <button class="logout-btn" @click="logOut">Log out</button>
      </div>
    </header>
    <main>
      <router-view />
    </main>
  </div>
</template>

<script setup lang="ts">
import { useAccount } from "jazz-vue";

const { me, logOut } = useAccount();
</script>

Subscribing to a CoValue

Subscribe to a CoValue inside src/views/HomeView.vue:

<script setup lang="ts">
import { Group, type ID } from "jazz-tools";
import { ref, toRaw, watch } from "vue";
import { computed } from "vue";
import { useAccount, useCoState } from "jazz-vue";
import { Folder, FolderList, ToDoItem, ToDoList } from "../schema";

const { me } = useAccount();

// Computed ID for the folders list
const computedFoldersId = computed(() => me.value?.root?.folders?.id);

// Load folders and nested values
const folders = useCoState(FolderList, computedFoldersId, [{ items: [{}] }]);

See the full example here.

Mutating a CoValue

Here's how to create a new folder:

// continues previous example

const createFolder = async (name: string) => {
  // Create a group owned by the current user
  const group = Group.create({ owner: me.value });

  // Create the folder
  const newFolder = Folder.create(
    {
      name,
      items: ToDoList.create([], { owner: group }),
    },
    { owner: group },
  );

  // Add the folder to the list of folders.
  // This change is sent to all connected clients and will be synced in real time.
  folders.value?.push(newFolder);
  newFolderName.value = "";
};

See the full example here.