Skip to main content

Vue Integration

GUN integrates naturally with Vue.js’s reactive data system, providing real-time synchronization across clients.

Installation

1

Install GUN

Add GUN to your Vue project:
npm install gun
# or
yarn add gun
2

Include GUN in your Vue app

Import GUN in your main.js or component:
import Gun from 'gun'
3

Initialize GUN

Create a GUN instance and connect to peers:
const gun = Gun(['http://localhost:8765/gun'])

Basic Usage

Here’s a complete example of a Todo app using GUN with Vue:
<!doctype html>
<html>
<head>
    <title>Todo App: Gun + Vue</title>
    <script src="https://unpkg.com/vue" type="text/javascript"></script>
    <script src="https://cdn.jsdelivr.net/npm/gun/gun.js"></script>
</head>
<body>

<div id="app">
    <div class="todo-wrapper">
        <h1>Todos App</h1>

        <input 
            class="add-todo" 
            type="text" 
            placeholder="Add new todo" 
            v-model="newTodo" 
            @keyup.enter="addTodo"
        >

        <h3>Active</h3>
        <div v-for="active in activeTodos">
            <label>
                <input 
                    type="checkbox" 
                    @click.prevent="completeTodo(active.key)"
                > 
                {{ active.todo.description }}
            </label>
        </div>

        <h5 v-show="completedTodos.length > 0" class="completed-header">
            Completed
        </h5>
 
        <div v-for="completed in completedTodos" @click="reopenTodo(completed.key)">
            <span class="completed">{{ completed.todo.description }}</span>
            <span class="completed-time">
                (completed at {{ (new Date(completed.todo.completed)).toUTCString() }})
            </span>
        </div>
    </div>
</div>

<script>
var gun = new Gun();
var todos = gun.get('todos');

new Vue({
    data: {
        todos: [],
        newTodo: ""
    },
    computed: {
        activeTodos: function() {
            return this.todos.filter(function(todo) {
                return todo.todo.completed === '';
            });
        },
        completedTodos: function() {
            return this.todos.filter(function(todo) {
                return todo.todo.completed !== '';
            });
        }
    },
    methods: {
        todoUpdated: function(todo, nodeKey) {
            if (/\D/.test(nodeKey)) {
                return;
            }

            var existingTodoIndex = this.todos.findIndex(function(todo) {
                return todo.key === nodeKey
            });
            var todoToStore = {
                todo: todo,
                key: nodeKey
            };
            if (existingTodoIndex === -1) {
                this.todos.push(todoToStore);
            } else {
                this.todos.splice(existingTodoIndex, 1, todoToStore);
            }
        },
        addTodo: function() {
            var newTodo = todos.get(Date.now().toString());
            newTodo.put({
                description: this.newTodo,
                completed: ''
            });
            todos.set(newTodo);
            this.newTodo = "";
        },
        completeTodo: function(key) {
            todos.get(key).put({
                completed: Date.now()
            });
        },
        reopenTodo: function(key) {
            todos.get(key).put({
                completed: ''
            });
        }
    },
    mounted: function() {
        // Subscribe to updates on the todos set
        todos.map().on(this.todoUpdated.bind(this));
    }
}).$mount('#app');
</script>

</body>
</html>

Vue 3 Composition API

For Vue 3 applications using the Composition API:
import { ref, onMounted, computed } from 'vue'
import Gun from 'gun'

export default {
  setup() {
    const gun = Gun()
    const todos = gun.get('todos')
    const todoList = ref([])
    const newTodo = ref('')

    onMounted(() => {
      todos.map().on((todo, key) => {
        if (key !== '_') {
          const index = todoList.value.findIndex(t => t.key === key)
          if (index === -1) {
            todoList.value.push({ key, ...todo })
          } else {
            todoList.value[index] = { key, ...todo }
          }
        }
      })
    })

    const addTodo = () => {
      if (newTodo.value.trim()) {
        todos.get(Date.now().toString()).put({
          text: newTodo.value,
          completed: false
        })
        newTodo.value = ''
      }
    }

    const deleteTodo = (key) => {
      todos.get(key).put(null)
    }

    return {
      todoList,
      newTodo,
      addTodo,
      deleteTodo
    }
  }
}

Creating a GUN Plugin for Vue

You can create a Vue plugin to make GUN available throughout your app:
// gun-plugin.js
import Gun from 'gun'

export default {
  install: (app, options) => {
    const gun = Gun(options.peers || [])
    
    // Make gun available via this.$gun
    app.config.globalProperties.$gun = gun
    
    // Make gun available via inject
    app.provide('gun', gun)
  }
}
Usage in main.js:
import { createApp } from 'vue'
import App from './App.vue'
import GunPlugin from './gun-plugin'

const app = createApp(App)

app.use(GunPlugin, {
  peers: ['http://localhost:8765/gun']
})

app.mount('#app')
Using in components:
export default {
  inject: ['gun'],
  mounted() {
    this.gun.get('myData').on(data => {
      console.log(data)
    })
  }
}

Vuex Integration

Integrate GUN with Vuex for centralized state management:
import Gun from 'gun'

const gun = Gun()

const store = {
  state: {
    todos: []
  },
  mutations: {
    SET_TODOS(state, todos) {
      state.todos = todos
    },
    ADD_TODO(state, todo) {
      state.todos.push(todo)
    },
    UPDATE_TODO(state, { key, todo }) {
      const index = state.todos.findIndex(t => t.key === key)
      if (index !== -1) {
        state.todos[index] = { key, ...todo }
      }
    }
  },
  actions: {
    initTodos({ commit }) {
      gun.get('todos').map().on((todo, key) => {
        if (key !== '_') {
          commit('UPDATE_TODO', { key, todo })
        }
      })
    },
    addTodo({ commit }, text) {
      const key = Date.now().toString()
      gun.get('todos').get(key).put({ text, completed: false })
    },
    toggleTodo({ commit }, key) {
      gun.get('todos').get(key).once(todo => {
        gun.get('todos').get(key).put({ completed: !todo.completed })
      })
    }
  }
}

Community Packages

Check out these community-maintained Vue + GUN packages:

Best Practices

  1. Use computed properties: Filter GUN data in Vue computed properties for reactive updates
  2. Clean up listeners: Remove GUN listeners when components are destroyed
  3. Avoid direct mutations: Use GUN’s .put() method to update data, not Vue’s reactive assignments
  4. Filter metadata: GUN uses _ keys for metadata - filter these in your display logic

Reactive Patterns

Two-way Data Binding

data() {
  return {
    username: ''
  }
},
watch: {
  username(newVal) {
    this.$gun.get('user').get('name').put(newVal)
  }
},
mounted() {
  this.$gun.get('user').get('name').on(name => {
    this.username = name
  })
}

Next Steps

Build docs developers (and LLMs) love