LearnAI ToolsCareerPractice BuildsPlayContact
Lesson 3515 min read

Fetching Data

Fetch data from an API in Vue components using the native fetch API, with proper loading and error states.

Fetching on Mount

The most common pattern: fetch data once, when the component mounts, storing the result in a ref.

<script setup>
import { ref, onMounted } from 'vue';
const posts = ref([]);
onMounted(async () => {
const res = await fetch('/api/posts');
posts.value = await res.json();
});
</script>
<template>
<ul>
<li v-for="post in posts" :key="post.id">{{ post.title }}</li>
</ul>
</template>

Loading and Error States

A production-ready fetch tracks three states explicitly: loading, error, and the data itself, so the template can show the right thing at each stage.

<script setup>
import { ref, onMounted } from 'vue';
const posts = ref([]);
const loading = ref(true);
const error = ref(null);
onMounted(async () => {
try {
const res = await fetch('/api/posts');
if (!res.ok) throw new Error('Failed to load posts');
posts.value = await res.json();
} catch (err) {
error.value = err.message;
} finally {
loading.value = false;
}
});
</script>
<template>
<p v-if="loading">Loading…</p>
<p v-else-if="error">{{ error }}</p>
<ul v-else>
<li v-for="post in posts" :key="post.id">{{ post.title }}</li>
</ul>
</template>
Extract This into a Composable

This exact loading/error/data pattern repeats across almost every fetch in an app — the composables lesson earlier in this course showed exactly how to extract it into a reusable useFetch() function.

Re-fetching When a Value Changes

onMounted only runs once. To re-fetch whenever a dependency (like a route param or a search term) changes, use watch or watchEffect instead.

<script setup>
import { ref, watch } from 'vue';
const searchTerm = ref('');
const results = ref([]);
watch(searchTerm, async (term) => {
if (!term) {
results.value = [];
return;
}
const res = await fetch(`/api/search?q=${term}`);
results.value = await res.json();
});
</script>

Common Beginner Mistakes

Forgetting to check res.ok before parsing the response

fetch() only rejects on a network failure, not an HTTP error status — always check response.ok and throw explicitly for 4xx/5xx responses.

Not resetting the loading state in a finally block

Without finally, an error path can leave loading stuck as true forever — always reset it in finally so both success and failure paths clear it.

FAQs

Either works fine — axios offers some convenience (automatic JSON parsing, interceptors), while fetch is built in with no extra dependency. Both integrate the same way with Vue's reactivity.

Yes — TanStack Query has an official Vue adapter, and libraries like Pinia Colada offer similar caching/refetching conveniences for larger apps.

Summary

Fetching data in Vue is plain JavaScript fetch() combined with refs for loading, error, and data state. Next, you'll add validation to the forms you built earlier in this course.

Next Lesson →

Form Validation