Should a large Vue SFC be split into separate template, script and style files using src imports, and what does that cost?
answer
- one attribute per block
- paths resolve like module requests
- script setup refuses src
- file types versus concerns
basics
~20 sUsually not. src loads a block from another file, but <script setup> cannot use src, nor can a normal <script> beside it, so you lose script setup. Colocation is the point: decompose into smaller components and composables instead.
solid answer
~50 sA `src` attribute makes a block load its content from another file, such as `<template src="./view.html">`, `<style src="./view.css">` or `<script src="./view.js">`. Paths follow module-request rules: relative paths start with `./`, and npm packages can be referenced; custom blocks accept `src` too. The costs are concrete: **`<script setup>` cannot use `src`**, and a normal `<script>` cannot either while `<script setup>` is present, so a split component falls back to a normal script with `setup()` or options. You also lose colocation, which the Vue docs defend: **separation of concerns is not separation of file types**. The pre-compilation and hot reloading remain. For a large component, the better answer is usually decomposition: smaller child components, composables for logic, shared style modules. `src` fits edge cases such as reusing an external stylesheet or a team that insists on file-type separation.
code
vue · 18 lines<!-- Allowed: template and styles from files, logic in a normal script -->
<template src="./ReportView.html"></template>
<script lang="ts">
import { defineComponent, ref } from 'vue'
export default defineComponent({
setup() {
const rows = ref<string[]>([])
return { rows }
},
})
</script>
<style scoped src="./ReportView.css"></style>
<!-- Rejected by the compiler:
<script setup src="./ReportView.setup.ts"></script>
-->go deeper
Recall that a block can load its content from a file with src, and that <script setup> cannot use it.
Explain the path rules, that src content compiles exactly like inline content, and that a normal script cannot use src while script setup exists.
Argue for decomposition over file-type splitting when a component grows, and identify the few cases where src genuinely helps.
Set the team convention on colocation versus file-type separation, knowing that choosing separation gives up script setup across the codebase.
## What `src` does Every SFC block can take a `src` attribute that loads the block's content from an external file instead of inline text: ```vue <template src="./ProductTable.html"></template> <script src="./ProductTable.js"></script> <style src="./ProductTable.css"></style> ``` The compiler treats the external content exactly as if it were inline, so templates are still pre-compiled and styles still get Vue's transforms. The SFC specification spells out the path rules: - `src` values follow the same resolution rules as module requests; - **relative paths must start with `./`**; - you can reference files in installed npm packages, such as `<style src="todomvc-app-css/index.css" />`; - custom blocks accept `src` too, for example `<unit-test src="./unit-test.js">`. ## The hard restriction The compiler enforces two rules that decide most splitting debates: 1. **`<script setup>` cannot use `src`.** The docs explain why: script setup code depends on the context of the SFC, and moving it into a plain `.js` or `.ts` file would confuse developers and tools. 2. **A normal `<script>` cannot use `src` while `<script setup>` is present.** The parser reports this as an error. So a component split across files cannot use `<script setup>` for its logic. It must fall back to a normal `<script>` with an options object or an explicit `setup()`, which loses the macros, the automatic template exposure and the closed-by-default instance. ## The design argument The Vue docs address the motivation directly. Splitting by **file type** is often defended as separation of concerns, but the docs argue that **separation of concerns is not equal to separation of file types**. In component-based UI, a component's template, logic and style are inherently coupled; colocating them makes the component more cohesive. Splitting them into three files keeps the coupling but hides it across files. | Approach | Keeps `<script setup>` | Reduces the component's size | Keeps colocation | |---|---|---|---| | `src` split into three files | no | no, only moves text | no | | extract child components | yes | yes | yes | | extract composables for logic | yes | yes, for script | yes | | move shared CSS into a stylesheet imported by several components | yes | partly | mostly | ## When `src` is the right tool - **Reusing a third-party stylesheet** as a component's style block, for example from an npm package. - **Large static templates** such as generated markup or legal text, where editing HTML separately is convenient and the script is small. - **A team convention** that requires file-type separation, accepting that `<script setup>` is off the table. - **Custom blocks** whose content is produced by another tool, such as test specs or documentation. ## What does not change with `src` It is worth being precise about what splitting does not affect, because candidates often overstate the cost: - **Compilation.** The external template is still pre-compiled into a render function, and external styles still get `scoped`, `module` and `v-bind()` handling. - **Hot reloading.** The docs note that even with separate files you keep hot reloading and pre-compilation. - **Languages.** A block that uses `src` can still declare `lang`, for example `<style src="./card.scss" lang="scss">`. What does change is the script side and the reading experience: the logic cannot use `<script setup>`, and a reviewer must open three files to understand one component. ## How to answer in an interview 1. State what `src` does and the path rules. 2. Name the restriction: no `src` with `<script setup>`, on either script block. 3. Argue from the cause: a file is large because the component does too much, so decompose it rather than redistribute its text. 4. Name the legitimate uses above, so the answer is a judgment, not a rule. ## Summary `src` moves a block's text into another file without changing how it compiles, but it rules out `<script setup>` and separates tightly coupled code. For an oversized component the fix is decomposition; `src` is for reusing external files and deliberate conventions.
- Why does the compiler refuse <script setup src="./logic.ts">?Script setup code relies on the context of an SFC: its top-level bindings feed the template, and macros like `defineProps` are compiled in place. Moved to a separate `.ts` file, that code would look like an ordinary module to developers and tools, so the compiler rejects `src` on `<script setup>` and, while script setup is present, on a normal `<script>` too.
- What should you do instead when one .vue file has grown to 1,500 lines?Decompose by concern rather than by file type: extract child components for distinct parts of the UI, move reusable stateful logic into composables, and move shared styling into stylesheets imported where needed. Each resulting SFC stays small and keeps `<script setup>` and colocation.
saying these in an interview costs you the question
- src on <script setup> is the clean way to move logic out of a .vue file.
- Blocks loaded through src are not pre-compiled.
- Relative src paths work without a leading ./.
- Splitting a component into three files reduces its complexity.
- Separation of concerns requires separate files for HTML, CSS and JS.