Connect Components
Learn how to integrate Craft CMS data with your Vue components.
This guide explains the steps to connect your Vue components with data from Craft CMS. We’ll create a custom fetch function, mapping object for components, query data, and use the <CraftPage/>
and <CraftArea/>
components to display content dynamically.
Custom Fetch Function
Before we can show data we should build a custom fetch function. For that you can make a new util.
export async function fetchData(url: string) {
const response = await fetch(url);
if (!response.ok) {
throw new Error(`Failed to fetch data from ${url}: ${response.statusText}`);
}
return await response.json();
}
Catch all route
To support dynamic URLs in your Vue app, add a catch-all route by following this guide. Your router.ts
file can look like that:
import { createRouter, createWebHistory } from 'vue-router';
import CraftRouter from './CraftRouter.vue'; // next step
export const router = createRouter({
history: createWebHistory(),
routes: [{ path: '/:pathMatch(.*)*', component: CraftRouter }],
});
CraftRouter Component
To handle the routing correctly you should now make a CraftRouter.vue
. This file is there to handle the routing Logic based on the router uri.
We watch if the uri changes and if so we update the uri variable. This is neccessary for the correct fetch to Craft CMS.
<script setup lang="ts">
import { ref, watch } from 'vue';
import { useRoute } from 'vue-router';
const route = useRoute();
const uri = ref(route.params.pathMatch || '__home__');
watch(
() => route.fullPath,
async () => {
uri.value = route.params.pathMatch || '__home__';
},
);
</script>
<template>
<div>
{{ uri }}
</div>
</template>
Mapping Object
Define a mapping object in the CraftRouter.vue
file. This object connects each Craft CMS section handle to a Vue page component and each field handle to a specific Vue component. This setup allows the correct component to render based on the Craft CMS data.
For example:
<script setup lang="ts">
import { ref, watch } from 'vue';
import { useRoute } from 'vue-router';
// some dummy components
import Home from './views/home.vue';
import News from './views/news.vue';
import Headline from './components/headline.vue';
// that's how you connect Craft with Vue
const mapping: Config = {
pages: {
home: Home,
news: News,
},
components: {
headline: Headline,
imageText: CraftNotImplemented,
},
};
const route = useRoute();
const uri = ref(route.params.pathMatch || '__home__');
watch(
() => route.fullPath,
async () => {
uri.value = route.params.pathMatch || '__home__';
},
);
</script>
<template>
<div>
{{ uri }}
</div>
</template>
Query Data
Use the fetchData()
helper to fetch data from Craft CMS.
Here’s how your CraftRouter.vue
looks now.
<script setup lang="ts">
import { ref, watch } from 'vue';
import { useRoute } from 'vue-router';
import { useCraftUrlBuilder, CraftNotImplemented, CraftPage, type Config } from 'vue-craftcms';
import { fetchData } from './utils/fetcher';
import Home from './views/home.vue';
import News from './views/news.vue';
import Headline from './components/headline.vue';
const mapping: Config = {
pages: {
home: Home,
news: News,
},
components: {
headline: Headline,
imageText: CraftNotImplemented,
},
};
const route = useRoute();
const uri = ref(route.params.pathMatch || '__home__');
const urlBuilder = useCraftUrlBuilder('entries'); // init here to use it in the right Vue context
const data = ref(await fetchData(urlBuilder.uri(uri.value).buildUrl('one'))); // on initial load
watch(
() => route.fullPath,
async () => {
uri.value = route.params.pathMatch || '__home__';
data.value = await fetchData(urlBuilder.uri(uri.value).buildUrl('one')); // on route change
},
);
</script>
<template>
<div>
<!-- Let the CraftPage handle the rest -->
<CraftPage :config="mapping" :content="data" />
</div>
</template>
This setup should render the correct Vue page based on your defined mapping object.
To verify the data structure Craft CMS sends to your page, you can add the following code to inspect the data in ./views/home.vue
:
<template>
<h1>Home</h1>
<pre>
{{ $attrs }}
</pre>
</template>
Display Components
To connect Matrix blocks with Vue components, use the <CraftArea/>
component. This component will dynamically render Vue components based on the content provided from Craft CMS.
Example:
<script setup lang="ts">
const props = defineProps({
metadata: {
type: Object,
required: true,
},
contentBuilder: {
type: Object,
required: true,
},
sectionHandle: {
type: String,
required: true,
},
title: {
type: String,
required: true,
},
});
</script>
<template>
<div>
<h1>{{ props.title }}</h1>
<CraftArea v-if="props.contentBuilder" :content="props.contentBuilder" />
</div>
</template>
To find out more about the <CraftArea/>
check out the docs.