Frontend Academy · Lesson

The setup() Function and script setup

Write logic in the setup() function or use the sugar, expose values to the template, and understand lifecycle hooks in Composition API.

Lesson 3 of 413 steps

The setup() Function and script setup is a free Frontend Academy lesson on CoddyKit. This is lesson 3 of 4. You can read the complete lesson below for free — then practise it hands-on in the browser with a built-in code editor and a 24/7 AI tutor. It is part of the Frontend Academy learning path, and your progress syncs across the web and the CoddyKit app. The Frontend Academy course includes 4 lessons in total.

The setup() Function

In Vue 3's Composition API, setup() is a component option that replaces data, methods, computed, and watch. It receives props and the setup context as arguments and returns what the template needs.

export default {
  props: { userId: String },
  setup(props, { emit, attrs, slots }) {
    const user = ref(null);
    onMounted(async () => {
      user.value = await fetchUser(props.userId);
    });
    return { user }; // must return what template uses
  }
};

<script setup> — Syntax Sugar

<script setup> is compile-time syntax sugar for setup(). Everything declared at the top level is automatically available in the template. No return statement needed.

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

const count = ref(0);
// count is automatically available in template
</script>

Lifecycle Hooks in setup()

Vue 3 lifecycle hooks are imported functions: onMounted, onUpdated, onUnmounted, onBeforeMount, onBeforeUpdate, etc. They can be called multiple times to register multiple handlers.

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

let timer: ReturnType<typeof setInterval>;

onMounted(() => {
  timer = setInterval(tick, 1000);
  console.log('Component mounted');
});

onUnmounted(() => {
  clearInterval(timer); // cleanup
});
</script>

setup() Props

In setup(), props are reactive. Don't destructure them directly — use toRefs(props) or access via props.x to maintain reactivity.

const props = defineProps<{ userId: string }>();

// Reactive access:
watch(() => props.userId, (id) => fetchUser(id));

// toRefs for destructuring:
const { userId } = toRefs(props);

defineExpose() — Exposing to Parent

By default,