skip to content

Why does Vue 3's Composition API have no onCreated hook, and which Options API hooks does `<script setup>` code replace?

level: middleimportance: should knowfreq 55%

answer

  1. setup runs before every option
  2. the script body is the hook
  3. beforeCreate and created
  4. beforeUnmount replaced beforeDestroy

basics

~20 s

setup() itself runs at the point where beforeCreate and created would, and in fact before both, so code written directly in <script setup> is the created phase. A separate onCreated hook would add nothing, so Vue does not provide one.

solid answer

~40 s

In the Options API, `beforeCreate` runs when the instance is initialised and `created` after data, computed, methods and watchers are set up, still before mounting. In the Composition API, `setup()` (the body of `<script setup>`) runs once at that same stage, and the Vue docs note it runs before any Options API hook, even `beforeCreate`. Whatever you would put in `created`, starting a request or initialising state, you write directly in the script body. That is why the Composition API has `onBeforeMount`, `onMounted` and so on but no `onBeforeCreate` or `onCreated`. On the teardown side, Vue 3 names the options `beforeUnmount` and `unmounted` (Vue 2's `beforeDestroy` and `destroyed`), with `onBeforeUnmount` and `onUnmounted` as the Composition API equivalents.

go deeper

for a junior

Recall that the body of script setup plays the role of beforeCreate and created, and that the Vue 3 teardown options are beforeUnmount and unmounted.

for a middle

Explain that setup runs before beforeCreate, what created guarantees in the Options API, and the order when both APIs are mixed in one component.

for a senior

Use the mixed-API order when migrating components incrementally, and catch reviews that wrap created-phase code in onBeforeMount and so delay it for no reason.

for a principal

Decide how far a codebase mixes the two APIs: mixing is supported, but a single convention keeps the lifecycle story simple for every reader.

## Two APIs, one lifecycle Vue 3 components can be written with the **Options API** (an object of `data`, `methods`, lifecycle options like `created` and `mounted`) or the **Composition API** (a `setup()` function, usually written as `<script setup>`, calling functions such as `onMounted`). Both drive the same internal lifecycle, so every Options API hook has a place in the Composition API. For the first two, that place is `setup()` itself. ## What beforeCreate and created mean - **`beforeCreate`**: called immediately when the instance is initialised and props are resolved, before `data()` and `computed` are set up. - **`created`**: called after the instance has processed all state-related options: reactive data, computed properties, methods and watchers exist. Mounting has not started, and `$el` is not available. Both exist so Options API code has a moment to run between "the instance exists" and "the instance renders". ## Why setup makes them redundant The Composition API **is** that moment. `setup()` runs once per instance, after props are resolved and before the first render. The Vue reference is explicit: the `setup()` hook of the Composition API is called **before any Options API hooks, even `beforeCreate()`**. So: 1. The props are already available in setup. 2. Reactive state you declare with `ref` or `reactive` exists as soon as the line runs. 3. Nothing has been rendered yet. A hypothetical `onCreated(() => { ... })` would run code at the point the script body is already running, so it would just be an extra indirection. Vue therefore exports `onBeforeMount`, `onMounted`, `onBeforeUpdate`, `onUpdated`, `onBeforeUnmount`, `onUnmounted` and a few others, but no creation hooks. ## Mapping table | Options API | Composition API (`<script setup>`) | |---|---| | `beforeCreate` | code in the script body (setup runs even earlier) | | `created` | code in the script body | | `beforeMount` | `onBeforeMount` | | `mounted` | `onMounted` | | `beforeUnmount` | `onBeforeUnmount` | | `unmounted` | `onUnmounted` | The last two rows are the Vue 3 names. Vue 2 called them `beforeDestroy` and `destroyed`; Vue 3 renamed them to match the mount/unmount vocabulary of the rest of the API. ## Mixing the two APIs in one component A component may define both `setup()` and Options API hooks. The order is then: - `setup()` first, - then `beforeCreate`, then `created`, - then `beforeMount`/`onBeforeMount` and the rest of the mount phase. A `created` option can read what `setup()` returned, but `setup()` cannot see anything the options create, because it runs before them. This matters when migrating a large component piece by piece. ## A migration example An Options API component that loads a profile when it is created: ```js export default { props: ['userId'], data() { return { profile: null } }, created() { loadProfile(this.userId).then(p => { this.profile = p }) }, beforeUnmount() { cancelPendingLoads() } } ``` becomes, in `<script setup>`: ```ts import { ref, onBeforeUnmount } from 'vue' // Profile, loadProfile and cancelPendingLoads come from the app's own module const props = defineProps<{ userId: string }>() const profile = ref<Profile | null>(null) loadProfile(props.userId).then(p => { profile.value = p }) // was created() onBeforeUnmount(() => cancelPendingLoads()) ``` The `created` body moves to the top level of the script unchanged in meaning; only the teardown needs a hook. ## Common misreadings - **"Code in `<script setup>` runs on every render."** It runs once per instance; only the template's render function re-runs on updates. - **"`onBeforeMount` is the Composition API's `created`."** It runs later, right before the first render; the script body is the equivalent. - **"`created` is gone in Vue 3."** It still exists as an Options API option; only the Composition API has no function form of it. ## How to phrase it in an interview State that `setup()` runs before `beforeCreate`, so the script body is where created-phase code goes, name the Vue 3 unmount option names, and mention that the Options API still supports `created` for components written in that style.

  • If a Vue 3 component defines both setup() and a created option, which runs first and what can each see?
    `setup()` runs first, before even `beforeCreate`. The `created` option runs afterwards and can access what `setup()` returned through `this`, along with data, computed and methods. `setup()` cannot access Options API state, because none of it exists yet when setup runs.
  • Does code at the top of a Vue 3 <script setup> block run again when the component re-renders?
    No. The script body is the component's `setup()`, which runs once per instance. Re-renders re-run only the compiled render function of the template, reading the reactive state that setup created. That is one of the key differences from function components that re-run their body on every render.

saying these in an interview costs you the question

  • onBeforeMount is the Composition API version of created
  • Vue 3 removed the created option entirely
  • setup runs after created, once data is initialised
  • Code in script setup runs on every re-render
  • beforeDestroy is still the Vue 3 option name for teardown