Vize

vize:croquis/cf/composable-outside-setup

A composable is called outside setup.

Default severity: Not emitted
Applies to: Analyzed component graph and the supported facts described below
Automatic fix: None; review related files and apply the repair
Options: No per-code options; supported CLI findings accept severity overrides

This is a published diagnostic contract without a current producer. The Bad/Good scenario below explains the risk and repair; no flag currently makes this code trigger.

The concern is this lifecycle-dependent composable, not a blanket ban on ordinary utility functions or all Composition API calls outside setup. This contract has no current producer.

Shared project files

Use these unchanged files in both Bad and Good. Install the imported packages in the project: Vue, plus vue-router or Pinia where shown. Follow any version-specific support note. The entry root makes the component relationship explicit.

main.ts

import { createApp } from 'vue';
import App from './App.vue';
createApp(App).mount('#app');

index.html

<!doctype html>
<html lang="en"><head><meta charset="UTF-8"><title>Contract scenario</title></head>
<body><div id="app"></div><script type="module" src="/main.ts"></script></body></html>

App.vue

<script setup lang="ts">
import { useTitle } from './use-title';
const title = useTitle();
</script>

<template>
<h1>{{ title }}</h1>
</template>

Bad

Importing use-title.ts registers onMounted before a component setup is active. Calling its exported function later only returns that module-level ref; it cannot repair the missed lifecycle ownership.

use-title.ts

import { onMounted, ref } from 'vue';
const title = ref('Before mount');
onMounted(() => { title.value = 'Mounted'; });
export function useTitle() { return title; }

Good

Both state creation and hook registration move into useTitle, which App calls synchronously inside setup. The mounted hook now belongs to that App instance.

use-title.ts

import { onMounted, ref } from 'vue';
export function useTitle() {
  const title = ref('Before mount');
  onMounted(() => { title.value = 'Mounted'; });
  return title;
}

The Good files demonstrate the change described above; other diagnostics can still apply to the complete project.

Public explanation

Cross-file index