Skip to main content
LiveCodes supports both Vue 2 and Vue 3 Single File Components with the official Vue compiler, including <script setup>, TypeScript, and scoped styles.

Configuration

Language Name: vue (Vue 3) or vue2 (Vue 2)
File Extensions: .vue, .vue3 (Vue 3), .vue2 (Vue 2)
Editor: Script editor
Compiler: Official Vue SFC compiler
Runtime: Vue 3.x runtime (automatic imports)

Vue 3 Single File Components

Basic Component

<template>
  <div class="counter">
    <h2>Count: {{ count }}</h2>
    <button @click="increment">Increment</button>
    <button @click="decrement">Decrement</button>
  </div>
</template>

<script>
import { ref } from 'vue';

export default {
  setup() {
    const count = ref(0);
    
    const increment = () => count.value++;
    const decrement = () => count.value--;
    
    return {
      count,
      increment,
      decrement,
    };
  },
};
</script>

<style scoped>
.counter {
  padding: 2rem;
  text-align: center;
}

button {
  margin: 0.5rem;
  padding: 0.5rem 1rem;
}
</style>

Script Setup

Use <script setup> for cleaner syntax:
<template>
  <div class="user-profile">
    <h1>{{ user.name }}</h1>
    <p>{{ user.email }}</p>
    <button @click="updateProfile">Update</button>
  </div>
</template>

<script setup>
import { ref, computed, onMounted } from 'vue';

const user = ref({
  name: 'John Doe',
  email: '[email protected]',
});

const displayName = computed(() => {
  return user.value.name.toUpperCase();
});

const updateProfile = () => {
  user.value.name = 'Jane Doe';
};

onMounted(() => {
  console.log('Component mounted!');
});
</script>

TypeScript Support

Use TypeScript in your Vue components:
<template>
  <div>
    <h2>{{ title }}</h2>
    <p>Count: {{ count }}</p>
    <button @click="handleClick">Increment</button>
  </div>
</template>

<script setup lang="ts">
import { ref, computed } from 'vue';

interface Props {
  title: string;
  initialCount?: number;
}

const props = withDefaults(defineProps<Props>(), {
  initialCount: 0,
});

const emit = defineEmits<{
  countChanged: [value: number];
}>();

const count = ref<number>(props.initialCount);

const handleClick = () => {
  count.value++;
  emit('countChanged', count.value);
};
</script>

Composition API

Full Composition API support:
<template>
  <div>
    <input v-model="searchQuery" placeholder="Search..." />
    <ul>
      <li v-for="item in filteredItems" :key="item.id">
        {{ item.name }}
      </li>
    </ul>
  </div>
</template>

<script setup>
import { ref, computed } from 'vue';

const searchQuery = ref('');
const items = ref([
  { id: 1, name: 'Apple' },
  { id: 2, name: 'Banana' },
  { id: 3, name: 'Cherry' },
]);

const filteredItems = computed(() => {
  return items.value.filter(item =>
    item.name.toLowerCase().includes(searchQuery.value.toLowerCase())
  );
});
</script>

Reactive State

Using ref, reactive, and computed:
<script setup>
import { ref, reactive, computed, watch } from 'vue';

// Ref for primitives
const count = ref(0);

// Reactive for objects
const state = reactive({
  user: {
    name: 'John',
    age: 30,
  },
  settings: {
    theme: 'dark',
  },
});

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

// Watchers
watch(count, (newValue, oldValue) => {
  console.log(`Count changed from ${oldValue} to ${newValue}`);
});

watch(
  () => state.user.name,
  (newName) => {
    console.log(`Name changed to ${newName}`);
  }
);
</script>

Vue 2 Support

Use Vue 2 SFC with the vue2 language:
<template>
  <div class="counter">
    <h2>Count: {{ count }}</h2>
    <button @click="increment">Increment</button>
  </div>
</template>

<script>
export default {
  data() {
    return {
      count: 0,
    };
  },
  methods: {
    increment() {
      this.count++;
    },
  },
  computed: {
    doubleCount() {
      return this.count * 2;
    },
  },
};
</script>

<style scoped>
.counter {
  padding: 1rem;
}
</style>

Template Syntax

Directives

<template>
  <div>
    <!-- Text interpolation -->
    <p>{{ message }}</p>
    
    <!-- Attribute binding -->
    <img :src="imageUrl" :alt="imageAlt" />
    
    <!-- Event listeners -->
    <button @click="handleClick">Click me</button>
    <input @input="handleInput" @keyup.enter="handleEnter" />
    
    <!-- Conditional rendering -->
    <p v-if="isVisible">Visible</p>
    <p v-else>Hidden</p>
    
    <!-- List rendering -->
    <ul>
      <li v-for="item in items" :key="item.id">
        {{ item.name }}
      </li>
    </ul>
    
    <!-- Two-way binding -->
    <input v-model="text" />
    
    <!-- Show/hide (CSS) -->
    <div v-show="isActive">Toggle visibility</div>
  </div>
</template>

Slots

Component composition with slots:
<template>
  <div class="card">
    <div class="card-header">
      <slot name="header">Default Header</slot>
    </div>
    <div class="card-body">
      <slot>Default content</slot>
    </div>
    <div class="card-footer">
      <slot name="footer" :data="footerData" />
    </div>
  </div>
</template>

<script setup>
const footerData = { date: new Date() };
</script>

Scoped Styles

Component-scoped CSS:
<template>
  <div class="button">
    <slot>Click me</slot>
  </div>
</template>

<style scoped>
.button {
  background: #3b82f6;
  color: white;
  padding: 0.5rem 1rem;
  border: none;
  border-radius: 0.25rem;
  cursor: pointer;
}

.button:hover {
  background: #2563eb;
}

/* Deep selector for nested components */
:deep(.nested-class) {
  color: red;
}

/* Slotted content */
:slotted(button) {
  margin: 0.5rem;
}
</style>

CSS Preprocessors

Use SCSS, Less, or Stylus:
<style scoped lang="scss">
$primary-color: #3b82f6;

.component {
  background: $primary-color;
  
  &:hover {
    background: darken($primary-color, 10%);
  }
  
  .nested {
    color: white;
  }
}
</style>

Component Communication

Props

<template>
  <div>
    <h2>{{ title }}</h2>
    <p>{{ description }}</p>
  </div>
</template>

<script setup>
const props = defineProps({
  title: {
    type: String,
    required: true,
  },
  description: {
    type: String,
    default: 'No description',
  },
});
</script>

Events

<template>
  <button @click="handleClick">Click me</button>
</template>

<script setup>
const emit = defineEmits(['submit', 'cancel']);

const handleClick = () => {
  emit('submit', { timestamp: Date.now() });
};
</script>

Provide/Inject

<!-- Parent component -->
<script setup>
import { provide, ref } from 'vue';

const theme = ref('dark');
provide('theme', theme);
</script>

<!-- Child component -->
<script setup>
import { inject } from 'vue';

const theme = inject('theme');
</script>

Lifecycle Hooks

<script setup>
import { 
  onMounted, 
  onUpdated, 
  onUnmounted,
  onBeforeMount,
  onBeforeUpdate,
  onBeforeUnmount,
} from 'vue';

onBeforeMount(() => {
  console.log('Before mount');
});

onMounted(() => {
  console.log('Mounted');
  // Fetch data, set up listeners, etc.
});

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

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

onUnmounted(() => {
  console.log('Unmounted');
});
</script>

Compiler Configuration

Configure the Vue compiler:
{
  "customSettings": {
    "vue": {
      "compilerOptions": {
        "isCustomElement": (tag) => tag.startsWith('my-')
      }
    }
  }
}

External Libraries

Import Vue ecosystem packages:
<script setup>
import { useRouter, useRoute } from 'vue-router';
import { useStore } from 'vuex';
import { useI18n } from 'vue-i18n';

const router = useRouter();
const route = useRoute();
const store = useStore();
const { t } = useI18n();
</script>

Example Projects

Todo App

<template>
  <div class="todo-app">
    <h1>Todo List</h1>
    <input 
      v-model="newTodo" 
      @keyup.enter="addTodo"
      placeholder="Add a todo..."
    />
    <ul>
      <li 
        v-for="todo in todos" 
        :key="todo.id"
        :class="{ done: todo.completed }"
      >
        <input 
          type="checkbox" 
          v-model="todo.completed"
        />
        {{ todo.text }}
        <button @click="removeTodo(todo.id)">Delete</button>
      </li>
    </ul>
  </div>
</template>

<script setup>
import { ref } from 'vue';

const newTodo = ref('');
const todos = ref([]);
let nextId = 1;

const addTodo = () => {
  if (newTodo.value.trim()) {
    todos.value.push({
      id: nextId++,
      text: newTodo.value,
      completed: false,
    });
    newTodo.value = '';
  }
};

const removeTodo = (id) => {
  todos.value = todos.value.filter(todo => todo.id !== id);
};
</script>

<style scoped>
.done {
  text-decoration: line-through;
  opacity: 0.6;
}
</style>

Data Fetching

<template>
  <div>
    <div v-if="loading">Loading...</div>
    <div v-else-if="error">Error: {{ error }}</div>
    <ul v-else>
      <li v-for="user in users" :key="user.id">
        {{ user.name }} - {{ user.email }}
      </li>
    </ul>
  </div>
</template>

<script setup>
import { ref, onMounted } from 'vue';

const users = ref([]);
const loading = ref(true);
const error = ref(null);

onMounted(async () => {
  try {
    const response = await fetch('https://jsonplaceholder.typicode.com/users');
    users.value = await response.json();
  } catch (e) {
    error.value = e.message;
  } finally {
    loading.value = false;
  }
});
</script>

Code Formatting

Automatic formatting with Prettier:
<!-- Before formatting -->
<template><div><h1>{{title}}</h1></div></template>

<!-- After formatting (Ctrl+Shift+F) -->
<template>
  <div>
    <h1>{{ title }}</h1>
  </div>
</template>

Best Practices

Prefer <script setup> for cleaner, more performant code:
<!-- Good -->
<script setup>
import { ref } from 'vue';
const count = ref(0);
</script>

<!-- Verbose -->
<script>
import { ref } from 'vue';
export default {
  setup() {
    const count = ref(0);
    return { count };
  }
};
</script>
Use ref for primitives, reactive for objects:
<script setup>
import { ref, reactive } from 'vue';

// Good for primitives
const count = ref(0);
const name = ref('John');

// Good for objects
const user = reactive({
  name: 'John',
  age: 30,
});
</script>
Always provide unique keys:
<!-- Good -->
<li v-for="item in items" :key="item.id">
  {{ item.name }}
</li>

<!-- Bad -->
<li v-for="(item, index) in items" :key="index">
  {{ item.name }}
</li>

Limitations

No SSR: Server-Side Rendering is not supported. All components are client-side rendered.
Vue Router and Vuex work but require manual configuration. State management libraries like Pinia are recommended.

React

Alternative framework with JSX

Svelte

Compiler-based framework

TypeScript

Use TypeScript in Vue components

SCSS

Use SCSS in style blocks

Build docs developers (and LLMs) love