Loading...

Vue online editor

Write, Run & Share Vue code online using OneCompiler's Vue online editor for free. It's one of the robust, feature-rich online editors for Vue. Getting started with the OneCompiler's Vue online editor is really simple and pretty fast. The editor shows sample boilerplate code when you choose language as 'Vue' and start writing code to learn and test online without worrying about tedious process of installation.

About Vue

Vue is a progressive JavaScript framework for building user interfaces. It features an approachable core library focused on the view layer, with supporting libraries for routing, state management, and build tooling. Vue's reactivity system automatically tracks dependencies and updates the DOM efficiently when data changes.

Syntax help

Template Syntax

Vue uses an HTML-based template syntax that allows you to declaratively bind the rendered DOM to the component's data. Templates are compiled into Virtual DOM render functions for optimal performance. Text interpolation uses double curly braces, while directives (prefixed with v-) add reactive behavior to elements. You can use any valid JavaScript expression inside bindings.

<!-- Text interpolation -->
<p>{{ message }}</p>
<p>{{ count + 1 }}</p>
<p>{{ isActive ? 'Yes' : 'No' }}</p>
<p>{{ message.toUpperCase() }}</p>

<!-- Raw HTML (use carefully, XSS risk) -->
<div v-html="rawHtml"></div>

<!-- Attribute binding -->
<img v-bind:src="imageUrl" />
<img :src="imageUrl" />
<!-- Shorthand -->
<a :href="link">Link</a>
<button :disabled="isDisabled">Click</button>

<!-- Dynamic attribute names -->
<a :[attributeName]="value">Link</a>

<!-- Boolean attributes (rendered if truthy, removed if falsy) -->
<button :disabled="isSubmitting">Submit</button>

<!-- Multiple attributes with object -->
<div v-bind="{ id: itemId, class: itemClass }"></div>

Reactivity and Data

Vue's reactivity system automatically tracks changes to component data and updates the DOM accordingly. In Vue 3, you use ref() for primitive values and reactive() for objects. The Composition API provides explicit reactivity declarations, making it easier to understand which data is reactive. Computed properties create derived values that automatically update when their dependencies change.

// Options API
export default {
  data() {
    return {
      count: 0,
      message: 'Hello',
      user: { name: 'John', age: 30 }
    }
  },
  computed: {
    doubleCount() {
      return this.count * 2
    },
    fullName: {
      get() { return this.firstName + ' ' + this.lastName },
      set(value) { [this.firstName, this.lastName] = value.split(' ') }
    }
  },
  watch: {
    count(newVal, oldVal) {
      console.log('Count changed:', oldVal, '->', newVal)
    }
  }
}

// Composition API (Vue 3)
import { ref, reactive, computed, watch } from 'vue'

export default {
  setup() {
    // Reactive primitives (access via .value)
    const count = ref(0)
    const message = ref('Hello')

    // Reactive objects
    const user = reactive({ name: 'John', age: 30 })

    // Computed
    const doubleCount = computed(() => count.value * 2)

    // Watch
    watch(count, (newVal, oldVal) => {
      console.log('Count changed:', oldVal, '->', newVal)
    })

    // Watch multiple sources
    watch([count, message], ([newCount, newMsg]) => {
      console.log('Values:', newCount, newMsg)
    })

    return { count, message, user, doubleCount }
  }
}

Directives

Directives are special attributes that apply reactive behavior to the DOM. The v-if, v-else, and v-show directives control element visibility, while v-for renders lists. The v-model directive creates two-way bindings on form inputs. Event handling uses v-on (or @ shorthand) with modifiers for common patterns like preventing default behavior or stopping propagation.

<!-- Conditional rendering -->
<div v-if="type === 'A'">Type A</div>
<div v-else-if="type === 'B'">Type B</div>
<div v-else>Other</div>

<!-- v-show (toggles display CSS, element stays in DOM) -->
<p v-show="isVisible">Visible content</p>

<!-- List rendering -->
<ul>
  <li v-for="item in items" :key="item.id">{{ item.name }}</li>
</ul>
<li v-for="(item, index) in items" :key="item.id">
  {{ index }}: {{ item.name }}
</li>
<li v-for="(value, key, index) in object" :key="key">{{ key }}: {{ value }}</li>
<span v-for="n in 10" :key="n">{{ n }}</span>

<!-- v-model (two-way binding) -->
<input v-model="message" />
<textarea v-model="text"></textarea>
<input type="checkbox" v-model="checked" />
<select v-model="selected">
  <option value="a">A</option>
  <option value="b">B</option>
</select>

<!-- v-model modifiers -->
<input v-model.lazy="msg" />
<!-- Sync on change instead of input -->
<input v-model.number="age" />
<!-- Cast to number -->
<input v-model.trim="msg" />
<!-- Trim whitespace -->

<!-- Event handling -->
<button v-on:click="handleClick">Click</button>
<button @click="handleClick">Click</button>
<!-- Shorthand -->
<button @click="count++">Increment</button>
<!-- Inline -->
<button @click="greet($event)">Greet</button>
<!-- Access event -->

<!-- Event modifiers -->
<form @submit.prevent="onSubmit">
  <!-- preventDefault -->
  <a @click.stop="onClick">
    <!-- stopPropagation -->
    <div @click.self="onClick">
      <!-- Only if target is self -->
      <button @click.once="onClick">
        <!-- Trigger once only -->
        <input @keyup.enter="submit" />
        <!-- Key modifier -->
        <input @keyup.ctrl.enter="submit" />
        <!-- Key combination -->
      </button>
    </div></a
  >
</form>

Components

Components are reusable Vue instances with their own template, data, and logic. In Vue 3, components can use the Composition API with <script setup> for a more concise syntax. Props pass data from parent to child, while events emit data back up to parents. Slots allow parent components to inject content into child component templates.

<!-- Single File Component (.vue) -->
<template>
  <div class="greeting">
    <h1>{{ title }}</h1>
    <p>{{ message }}</p>
    <button @click="$emit('greet', name)">Say Hello</button>
  </div>
</template>

<script setup>
  import { ref } from "vue"

  // Props
  const props = defineProps({
    title: String,
    message: { type: String, required: true, default: "Hello" },
    count: { type: Number, validator: (v) => v >= 0 },
  })

  // Emits
  const emit = defineEmits(["greet", "update"])

  // Local state
  const name = ref("World")
</script>

<style scoped>
  .greeting {
    padding: 20px;
  }
</style>

<!-- Using the component -->
<Greeting title="Welcome" message="Hello there!" @greet="handleGreet" />

<!-- Slots -->
<template>
  <div class="card">
    <slot></slot>
    <!-- Default slot -->
    <slot name="header"></slot>
    <!-- Named slot -->
    <slot name="item" :item="item"></slot>
    <!-- Scoped slot -->
  </div>
</template>

<!-- Using slots -->
<Card>
  <template #header>Card Title</template>
  <p>Card content</p>
  <template #item="{ item }">{{ item.name }}</template>
</Card>

Lifecycle Hooks

Lifecycle hooks are functions that run at specific stages of a component's existence, from creation to destruction. In the Options API, hooks are defined as component options, while the Composition API uses imported functions. Common hooks include onMounted for DOM-dependent setup, onUpdated for post-render logic, and onUnmounted for cleanup like removing event listeners.

// Options API
export default {
  beforeCreate() { },    // Before reactive data setup
  created() { },         // After reactive data setup, before mount
  beforeMount() { },     // Before initial render
  mounted() { },         // After mounted to DOM
  beforeUpdate() { },    // Before re-render
  updated() { },         // After re-render
  beforeUnmount() { },   // Before unmount (Vue 3) / beforeDestroy (Vue 2)
  unmounted() { }        // After unmount (Vue 3) / destroyed (Vue 2)
}

// Composition API
import {
  onBeforeMount, onMounted,
  onBeforeUpdate, onUpdated,
  onBeforeUnmount, onUnmounted
} from 'vue'

export default {
  setup() {
    onBeforeMount(() => {
      console.log('Before mount')
    })

    onMounted(() => {
      console.log('Mounted')
      // Access DOM, start timers, fetch data
    })

    onBeforeUpdate(() => {
      console.log('Before update')
    })

    onUpdated(() => {
      console.log('Updated')
    })

    onBeforeUnmount(() => {
      console.log('Before unmount')
    })

    onUnmounted(() => {
      console.log('Unmounted')
      // Cleanup: remove listeners, clear timers
    })
  }
}

Composables

Composables are functions that encapsulate and reuse stateful logic using the Composition API. They can contain reactive state, computed properties, watchers, and lifecycle hooks, providing a clean way to share logic across components. Unlike mixins, composables have explicit inputs and outputs, making data flow clear and avoiding naming conflicts.

// useCounter.js - Reusable counter logic
import { ref, computed } from 'vue'

export function useCounter(initialValue = 0) {
  const count = ref(initialValue)

  const doubleCount = computed(() => count.value * 2)

  function increment() {
    count.value++
  }

  function decrement() {
    count.value--
  }

  function reset() {
    count.value = initialValue
  }

  return { count, doubleCount, increment, decrement, reset }
}

// useFetch.js - Reusable data fetching
import { ref, watchEffect } from 'vue'

export function useFetch(url) {
  const data = ref(null)
  const error = ref(null)
  const loading = ref(true)

  watchEffect(async () => {
    loading.value = true
    try {
      const response = await fetch(url.value || url)
      data.value = await response.json()
    } catch (e) {
      error.value = e
    } finally {
      loading.value = false
    }
  })

  return { data, error, loading }
}

// Using composables in component
import { useCounter } from './useCounter'
import { useFetch } from './useFetch'

export default {
  setup() {
    const { count, increment, decrement } = useCounter(10)
    const { data, loading, error } = useFetch('/api/users')

    return { count, increment, decrement, data, loading, error }
  }
}

Vue Router

Vue Router is the official router for Vue applications, enabling navigation between views without page reloads. Routes map URL paths to components, supporting dynamic segments, nested routes, and navigation guards. The router provides <router-link> for declarative navigation and <router-view> to render matched components.

// router.js
import { createRouter, createWebHistory } from "vue-router"

import Home from "./views/Home.vue"
import User from "./views/User.vue"

const routes = [
  { path: "/", component: Home },
  { path: "/about", component: () => import("./views/About.vue") }, // Lazy load
  { path: "/users/:id", component: User, props: true }, // Dynamic segment, pass as prop
  {
    path: "/dashboard",
    component: Dashboard,
    children: [
      { path: "profile", component: Profile },
      { path: "settings", component: Settings },
    ],
  },
  { path: "/:pathMatch(.*)*", component: NotFound }, // Catch-all 404
]

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

// Navigation guards
router.beforeEach((to, from) => {
  if (to.meta.requiresAuth && !isAuthenticated()) {
    return "/login"
  }
})

export default router
<!-- Template usage -->
<nav>
  <router-link to="/">Home</router-link>
  <router-link :to="{ name: 'user', params: { id: 123 }}">User</router-link>
</nav>
<router-view></router-view>

<!-- Programmatic navigation -->
<script setup>
  import { useRoute, useRouter } from "vue-router"

  const router = useRouter()
  const route = useRoute()

  // Navigate
  router.push("/users/123")
  router.push({ name: "user", params: { id: 123 } })
  router.replace("/login")
  router.go(-1)

  // Access route info
  console.log(route.params.id)
  console.log(route.query.search)
  console.log(route.path)
</script>

State Management (Pinia)

Pinia is the official state management library for Vue 3, providing a simple and type-safe way to share state across components. Stores contain state, getters (computed properties), and actions (methods) that can be accessed from any component. Unlike Vuex, Pinia has a flatter structure without mutations, making it more intuitive and easier to use with TypeScript.

// stores/counter.js
import { defineStore } from 'pinia'

export const useCounterStore = defineStore('counter', {
  state: () => ({
    count: 0,
    name: 'Counter'
  }),

  getters: {
    doubleCount: (state) => state.count * 2,
    // Access other getters with this
    doubleCountPlusOne() {
      return this.doubleCount + 1
    }
  },

  actions: {
    increment() {
      this.count++
    },
    async fetchCount() {
      const response = await fetch('/api/count')
      this.count = await response.json()
    }
  }
})

// Setup syntax alternative
export const useCounterStore = defineStore('counter', () => {
  const count = ref(0)
  const doubleCount = computed(() => count.value * 2)

  function increment() {
    count.value++
  }

  return { count, doubleCount, increment }
})
<!-- Using store in component -->
<script setup>
  import { useCounterStore } from "@/stores/counter"

  const counter = useCounterStore()
</script>

<template>
  <p>Count: {{ counter.count }}</p>
  <p>Double: {{ counter.doubleCount }}</p>
  <button @click="counter.increment()">+1</button>
</template>