Pada artikel kali ini, saya akan sharing bagaimana membuat personal blog menggunakan VueJS dan Tailwind CSS dengan memanfaatkan Notion API.
Detail artikel dari Notion API
Inisiasi Project
Requirement
Silahkan install terlebih dahulu nodejs dan npm di link berikut https://nodejs.org/en/download/
Instalasi vue dengan vite
Jalankan di cmd untuk menginstall vue dengan vite
1
npm create vite@latest personal-blog-notion -- --template vue
Setelah terinstall, ketik di cmd
1
2
3
cd personal-blog-notion
npm install
npm run dev
Tunggu beberapa menit, proses instalasi akan selesai. Setelah selesai jalankan npm run dev
dan buka di browser
1
2
3
4
5
VITE v5.0.0 ready in 487 ms
➜ Local: http://localhost:5173/
➜ Network: use --host to expose
➜ press h + enter to show help
Tampilan awal project vite+vue
Install tailwind
Install dependency di cmd dan tunggu untuk menginstall tailwindcss
1
npm install -D tailwindcss@latest postcss@latest autoprefixer@latest
Setelah berhasil, generate file tailwind.config.js
and postcss.config.js
1
npx tailwindcss init -p
Edit file tailwind.config.js
1
2
3
4
5
6
7
8
9
10
11
module.exports = {
purge: ['./index.html', './src/**/*.{vue,js,ts,jsx,tsx}'],
darkMode: false, // or 'media' or 'class'
theme: {
extend: {},
},
variants: {
extend: {},
},
plugins: [],
}
Kemudian, edit file style.css
dengan kode berikut. Kode berikut digunakan untuk menggunakan directive dari Tailwind dan akan dibuild secara otomatis ketika kita menjalankan npm run dev
1
2
3
@tailwind base;
@tailwind components;
@tailwind utilities;
Stop npm run dev
dan jalankan kembali di terminal. Jika tampilan berubah seperti ini, maka itu tandanya tailwind sudah berhasil kita install
Kita juga bisa coba menambahkan heading untuk recheck ulang apakah tailwind sudah benar terinstall.
1
2
3
4
5
6
7
8
9
10
11
12
<template>
<p class="text-3xl">Ini Heading</p>
<div>
<a href="https://vitejs.dev" target="_blank">
<img src="/vite.svg" class="logo" alt="Vite logo" />
</a>
<a href="https://vuejs.org/" target="_blank">
<img src="./assets/vue.svg" class="logo vue" alt="Vue logo" />
</a>
</div>
<HelloWorld msg="Vite + Vue" />
</template>
Tulisan “Ini Heading” sudah berubah, sehingga tailwind sudah berhasil kita install
Install vue-router
Selanjutnya, kita akan membuat router agar vue menjadi SPA (Single Page Application) atau user bisa berpindah dari satu halaman ke halaman lainnya tanpa reload ulang halaman secara keseluruhan.
Sebelum kita menginstall dependency, buat folder pages
di dalam folder src
dan tambahkan file src/pages/Home.vue
dan src/pages/blog/BlogDetail.vue
dan isi dengan kode di bawah ini.
1
2
3
4
5
6
7
8
9
10
11
<template lang="">
<div>
<h1 class="text-3xl">Homepage</h1>
</div>
</template>
<script>
export default {
}
</script>
<style lang="">
</style>
1
2
3
4
5
6
7
8
9
10
<template lang="">
<div>
<h1 class="text-3xl">Blog Detail </h1>
</div>
</template>
<script>
export default {
};
</script>
<style lang=""></style>
Install dependency di terminal
1
npm install vue-router@next --save
Buat file baru di src/router.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import { createRouter, createWebHistory } from "vue-router"
import Home from "./pages/Home.vue";
import BlogDetail from "./pages/blog/BlogDetail.vue";
const routeInfos = [
{
path: "/",
component: () => Home
},
{
path: "/blog/:id",
component: () => BlogDetail
}
]
const router = createRouter({
history: createWebHistory(),
routes: routeInfos
})
export default router;
Edit file src/main.js
untuk menggunakan router
1
2
3
4
5
6
7
8
import { createApp } from 'vue'
import './style.css'
import App from './App.vue'
import router from "./router"
createApp(App)
.use(router)
.mount('#app')
Lalu, edit isi file src/App.vue
dengan kode berikut
1
2
3
4
5
6
<template>
<h1>Welcome to my Personal Blog!</h1>
<router-link to="/">Home</router-link> |
<router-link to="/blog/123">Blog Detail</router-link>
<router-view />
</template>
Sekarang, coba buka di browser kembali. Jika tampilan berubah seperti ini, maka itu tandanya vue-router sudah berhasil kita install.
Tampilan blog detail dengan id = 123
Tampilan blog detail dengan id = 45
Install vue icons dengan oh-vue-icons
Jalankan command berikut di cmd
1
npm install oh-vue-icons
Oh Vue Icons memiliki lebih dari 30rb icons yang bisa langsung dipakai di project kita. Oh Vue Icons tidak meload semua icons ketika website dibuild, hanya icon yang akan kita pakai saja dalam project kita. Hal ini membantu agar ukuran aplikasi tidak berat dari sisi user. Untuk dokumentasi lengkapnya di link berikut, https://oh-vue-icons.js.org/
Setelah terinstall, edit file vite.config.js
dengan kode berikut
1
2
3
4
5
6
7
8
9
10
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
// https://vitejs.dev/config/
export default defineConfig({
plugins: [vue()],
optimizeDeps: {
exclude: ["oh-vue-icons/icons"]
}
})
Edit juga file main.js
dengan kode berikut untuk mendaftarkan list font-awesome icons dan material icons
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import { createApp } from 'vue'
import './style.css'
import App from './App.vue'
import router from "./router"
// added font-awesome & material icons
import { OhVueIcon, addIcons } from "oh-vue-icons";
import * as FaIcons from "oh-vue-icons/icons/fa";
import * as MdIcons from "oh-vue-icons/icons/md";
const Fa = Object.values({ ...FaIcons });
const Md = Object.values({ ...MdIcons });
addIcons(...Md);
addIcons(...Fa);
const app = createApp(App);
app.component("v-icon", OhVueIcon);
app.use(router);
app.mount("#app");
Insight
Kode di atas adalah contoh kode bad practice karena akan membuat size bundle aplikasi menjadi besar dan user yang membuka page kita akan terasa lambat.
Di real case, jangan load semua icon yang ada di library. Namun, pakai sesuai kebutuhan icon mana yang mau diload untuk mengurangi ukuran bundle dari aplikasi.
Icons, sudah berhasil diinstall. Untuk menggunakan icons, kita edit halaman Home.vue
dengan kode berikut
1
2
3
4
5
6
7
8
9
10
<template lang="">
<div>
<v-icon name="md-home" fill="green" scale="2" />
<h1 class="text-3xl">Homepage</h1>
</div>
</template>
<script>
export default {};
</script>
<style lang=""></style>
Oh Vue Icons sudah berhasil diinstall
Tada, kita sudah berhasil untuk menginstall semua dependency yang dibutuhkan, selanjutnya, kita akan fokus untuk membuat UI dengan Tailwind CSS menggunakan component based style.
Membangun UI dengan Tailwind
Edit file App.vue
dengan kode berikut
1
2
3
<template>
<router-view />
</template>
Tampilan Home
Ubah file Home.vue
dengan kode berikut,
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
<template lang="">
<div className="mx-[200px] pb-10">
<div className="flex flex-col items-center mt-14 mb-24">
<div className="w-[100px] h-[100px] rounded-full overflow-hidden">
<img
src="https://images.pexels.com/photos/774909/pexels-photo-774909.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=2"
/>
</div>
<div className="flex gap-2 text-[#333333] mt-4">
<a href="https://instagram.com" target="_blank">
<v-icon name="fa-instagram" fill="black" scale="1.5" />
</a>
<a href="https://twitter.com" target="_blank">
<v-icon name="fa-twitter" fill="black" scale="1.5" />
</a>
</div>
<div className="flex items-center gap-1.5 mt-2">
<h1 className="text-4xl font-bold">Bu Prani</h1>
<div className="w-4 h-4 bg-[#E99355] rounded-full mt-2"></div>
</div>
</div>
<div className="flex flex-col gap-16">
<div className="flex gap-14" v-for="i in 3" :key="i">
<router-link to="/blog/123" className="w-[400px] h-[200px] ">
<img
src="https://images.pexels.com/photos/147411/italy-mountains-dawn-daybreak-147411.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=2"
alt=""
className="rounded-lg object-cover object-center w-full h-full"
/>
</router-link>
<div className="">
<router-link to="/blog/123" className="text-xl font-medium">
Instagram head says Threads API is in the works
</router-link>
<div className="text-xs flex items-center gap-2 mt-4 mb-6">
<v-icon name="fa-calendar" />
<p>4/11/2023</p>
</div>
<p className="text-base mb-6">
Instagram head Adam Mosseri said today that a Threads
API is in the works. This will give chance to developers
to create different apps and experiences around Threads.
Mosseri was responding to jou…
</p>
<router-link
to="/blog/123"
className="flex items-center gap-2 text-xs font-semibold text-[#e99355]"
>
<p>Continue reading</p>
<v-icon name="fa-chevron-right" scale="0.7" />
</router-link>
</div>
</div>
</div>
</div>
</template>
<script>
export default {};
</script>
<style lang=""></style>
Tampilan Blog Detail
Setelah itu, ubah file BlogDetail.vue
dengan kode berikut,
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
<template lang="">
<div>
<div className="mx-[200px]">
<div className="h-[520px]">
<img
src="https://images.pexels.com/photos/147411/italy-mountains-dawn-daybreak-147411.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=2"
className="w-full h-full object-cover object-center"
/>
</div>
<div className="max-w-[673px] mx-auto">
<div className="">
<h1
className="text-[#333] text-5xl font-medium text-center mt-4 mb-3"
>
Instagram head says Threads API is in the works
</h1>
<div className="flex gap-3 items-center justify-center">
<div
className="flex items-center gap-2 text-xs text-[#333]"
>
<v-icon name="fa-calendar" fill="black" />
<p>4/11/2023</p>
</div>
<div
className="flex items-center gap-2 text-xs text-[#333]"
>
<v-icon name="fa-user" fill="black" />
<p>Alicia Stones</p>
</div>
</div>
</div>
<div className="mt-16">
Content dari notion {/* <NotionRenderer /> */}
</div>
</div>
</div>
</div>
</template>
<script>
export default {};
</script>
<style lang=""></style>
Tampilan blog detail dengan tailwind
Ubah ke component-based
Di halaman Home, kita akan memisahkan tampilan dari Header dan juga BlogList menjadi file terpisah. Hal ini dilakukan karena kita mengikuti prinsip component based dimana komponen UI dipecah menjadi komponen sendiri.
Edit file vite.config.js
agar dapat menggunakan alias @
di komponen
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
// https://vitejs.dev/config/
export default defineConfig({
plugins: [vue()],
resolve: {
alias: {
'@': '/src',
},
},
optimizeDeps: {
exclude: ["oh-vue-icons/icons"]
}
})
Buat file baru di folder components
:
Header.vue
BlogList.vue
Edit file Header.vue
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
<template lang="">
<div className="flex flex-col items-center mt-14 mb-24">
<div className="w-[100px] h-[100px] rounded-full overflow-hidden">
<img
src="https://images.pexels.com/photos/774909/pexels-photo-774909.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=2"
/>
</div>
<div className="flex gap-2 text-[#333333] mt-4">
<a href="https://instagram.com" target="_blank">
<v-icon name="fa-instagram" fill="black" scale="1.5" />
</a>
<a href="https://twitter.com" target="_blank">
<v-icon name="fa-twitter" fill="black" scale="1.5" />
</a>
</div>
<div className="flex items-center gap-1.5 mt-2">
<h1 className="text-4xl font-bold">Bu Prani</h1>
<div className="w-4 h-4 bg-[#E99355] rounded-full mt-2"></div>
</div>
</div>
</template>
<script>
export default {
name: "Header"
};
</script>
<style lang=""></style>
Edit file BlogList.vue
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
<template lang="">
<div className="flex gap-14">
<router-link to="/blog/123" className="w-[400px] h-[200px] ">
<img
src="https://images.pexels.com/photos/147411/italy-mountains-dawn-daybreak-147411.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=2"
alt=""
className="rounded-lg object-cover object-center w-full h-full"
/>
</router-link>
<div className="">
<router-link to="/blog/123" className="text-xl font-medium">
Instagram head says Threads API is in the works
</router-link>
<div className="text-xs flex items-center gap-2 mt-4 mb-6">
<v-icon name="fa-calendar" />
<p>4/11/2023</p>
</div>
<p className="text-base mb-6">
Instagram head Adam Mosseri said today that a Threads API is in
the works. This will give chance to developers to create
different apps and experiences around Threads. Mosseri was
responding to jou…
</p>
<router-link
to="/blog/123"
className="flex items-center gap-2 text-xs font-semibold text-[#e99355]"
>
<p>Continue reading</p>
<v-icon name="fa-chevron-right" scale="0.7" />
</router-link>
</div>
</div>
</template>
<script>
export default {
name: "BlogList",
};
</script>
<style lang=""></style>
Sekarang, edit file Home.vue
untuk memanggil komponen yang telah dibuat.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<template lang="">
<div className="mx-[200px] pb-10">
<Header />
<div className="flex flex-col gap-16">
<div v-for="i in 3" :key="i">
<BlogList />
</div>
</div>
</div>
</template>
<script setup>
import Header from "@/components/Header.vue";
import BlogList from "@/components/BlogList.vue";
</script>
<style lang=""></style>
Reload ulang halaman website
Edit BlogList agar dinamis
Edit file BlogList.vue
agar dapat menerima props
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
<template lang="">
<div className="flex gap-14">
<router-link to="/blog/123" className="w-[400px] h-[200px] ">
<img
:src="image"
alt=""
className="rounded-lg object-cover object-center w-full h-full"
/>
</router-link>
<div className="">
<router-link to="/blog/123" className="text-xl font-medium">
</router-link>
<div className="text-xs flex items-center gap-2 mt-4 mb-6">
<v-icon name="fa-calendar" />
<p></p>
</div>
<p className="text-base mb-6">
</p>
<router-link
to="/blog/123"
className="flex items-center gap-2 text-xs font-semibold text-[#e99355]"
>
<p>Continue reading</p>
<v-icon name="fa-chevron-right" scale="0.7" />
</router-link>
</div>
</div>
</template>
<script>
export default {
name: "BlogList",
props: {
id: {
type: String,
},
title: {
type: String,
},
date: {
type: String,
},
description: {
type: String,
default:
"Lorem ipsum dolor sit amet consectetur adipisicing elit. Consequatur, error. Lorem ipsum dolor sit amet consectetur adipisicing elit. Consequatur, error. Lorem ipsum dolor sit amet consectetur adipisicing elit. Consequatur, error.",
},
image: {
type: String,
default:
"https://images.pexels.com/photos/147411/italy-mountains-dawn-daybreak-147411.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=2",
},
},
};
</script>
<style lang=""></style>
Kemudian, edit file Home.vue
untuk menyesuaikan dengan data yang diberikan
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<template lang="">
<div className="mx-[200px] pb-10">
<Header />
<div className="flex flex-col gap-16">
<div v-for="i in 3" :key="i">
<BlogList
:id="i.toString()"
:title="`Blog ${i}`"
:date="`4/11/2023`"
/>
</div>
</div>
</div>
</template>
<script setup>
import Header from "@/components/Header.vue";
import BlogList from "@/components/BlogList.vue";
</script>
<style lang=""></style>
Komponen BlogList sudah dinamis
API dari Notion
Buat akun terlebih dahulu di notion.so. Setelah terdaftar, buka halaman notion blog berikut dan klik tombol Duplicate
Setup router
Edit file router.js
untuk menambahkan field name dan params slug di blog detail
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import { createRouter, createWebHistory } from "vue-router"
import Home from "./pages/Home.vue";
import BlogDetail from "./pages/blog/BlogDetail.vue";
const routeInfos = [
{
name: 'home',
path: "/",
component: () => Home
},
{
name: 'blogDetail',
path: "/blog/:slug",
component: () => BlogDetail
}
]
const router = createRouter({
history: createWebHistory(),
routes: routeInfos
})
export default router;
Buat Blog Service
Buat folder & file baru di src/services/blog.service.js
1
2
3
4
5
6
7
8
9
10
11
12
13
export const getBlogList = async () => {
return await fetch(
"https://notion-api.splitbee.io/v1/table/{id_notion}",
)
.then((res) => res.json())
.then((result) => result);
};
export const getBlogDetail = async (id) => {
return await fetch(`https://notion-api.splitbee.io/v1/page/${id}`)
.then((res) => res.json())
.then((result) => result);
};
Ganti {id_notion}
dengan ID notion pada url. Misalnya urlnya notehuda.notion.site/notehuda/1d30a536b01f4145a60127c2ebe829aa?v=bfa7df3f9e874a8d9193fe5ed1f970b2 maka ID nya adalah 1d30a536b01f4145a60127c2ebe829aa
Sehingga fungsi getBlogList
tersebut akan berisi kode seperti berikut
1
2
3
4
5
6
7
export const getBlogList = async () => {
return await fetch(
"https://notion-api.splitbee.io/v1/table/1d30a536b01f4145a60127c2ebe829aa",
)
.then((res) => res.json())
.then((result) => result);
};
Install vue-notion
Install vue-notion via cmd. Vue-notion ini akan berfungsi untuk merender result dari API notion ke dalam HTML.
1
npm install vue-notion@3.0.0-beta.1
Edit home
Edit file Home.vue
untuk menampilkan list artikel dari notion ke dalam component BlogList
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
<template lang="">
<div className="mx-[200px] pb-10">
<Header />
<div v-if="isBlogEmpty">Loading...</div>
<div className="flex flex-col gap-16" v-else>
<div v-for="blog in blogList" :key="blog.id">
<BlogList
:id="blog.id"
:slug="blog.slug"
:title="blog.title"
:date="blog.created_date"
:description="blog.description"
:image="blog.cover[0].url"
/>
</div>
</div>
</div>
</template>
<script setup>
import Header from "@/components/Header.vue";
import BlogList from "@/components/BlogList.vue";
import { ref, onMounted, computed } from "vue";
import { getBlogList } from "@/services/blog.service.js";
const blogList = ref([]);
onMounted(async () => {
try {
const result = await getBlogList();
blogList.value = result;
} catch (error) {
console.error("Error fetching blog list:", error);
}
});
// added function check blogList is not empty
const isBlogEmpty = computed(() => {
return blogList.value.length == 0;
});
</script>
<style lang=""></style>
Edit BlogList
Edit file BlogList.vue
untuk menampilkan data pada tiap artikel pada blog
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
<template lang="">
<div className="flex gap-14">
<div @click="goToBlogDetail" className="w-[400px] h-[200px] ">
<img
:src="image"
alt=""
className="rounded-lg object-cover object-center w-full h-full"
/>
</div>
<div className="">
<div @click="goToBlogDetail" className="text-xl font-medium">
</div>
<div className="text-xs flex items-center gap-2 mt-4 mb-6">
<v-icon name="fa-calendar" />
<p></p>
</div>
<p className="text-base mb-6">
</p>
<div
@click="goToBlogDetail"
className="flex items-center gap-2 text-xs font-semibold text-[#e99355]"
>
<p>Continue reading</p>
<v-icon name="fa-chevron-right" scale="0.7" />
</div>
</div>
</div>
</template>
<script>
import { useRouter } from "vue-router";
export default {
name: "BlogList",
props: {
id: {
type: String,
},
slug: {
type: String,
},
title: {
type: String,
},
date: {
type: String,
},
description: {
type: String,
default:
"Lorem ipsum dolor sit amet consectetur adipisicing elit. Consequatur, error. Lorem ipsum dolor sit amet consectetur adipisicing elit. Consequatur, error. Lorem ipsum dolor sit amet consectetur adipisicing elit. Consequatur, error.",
},
image: {
type: String,
default:
"https://images.pexels.com/photos/147411/italy-mountains-dawn-daybreak-147411.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=2",
},
},
setup(props) {
const router = useRouter(); // Initialize the router (push, etc)
const goToBlogDetail = () => {
router.push({
name: "blogDetail",
params: { slug: props.slug },
});
};
return {
goToBlogDetail,
};
},
};
</script>
<style lang=""></style>
Edit BlogDetail
Edit file BlogDetail.vue
untuk menampilkan detail artikel dari blog
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
<template lang="">
<div>
<div v-if="!blogDetail && !article">Loading...</div>
<div className="mx-[200px]" v-else>
<div className="h-[520px]">
<img
:src="blogDetail.cover[0].url"
className="w-full h-full object-cover object-center"
/>
</div>
<div className="max-w-[673px] mx-auto">
<div className="">
<h1
className="text-[#333] text-5xl font-medium text-center mt-4 mb-3"
>
</h1>
<div className="flex gap-3 items-center justify-center">
<div
className="flex items-center gap-2 text-xs text-[#333]"
>
<v-icon name="fa-calendar" fill="black" />
<p></p>
</div>
<div
className="flex items-center gap-2 text-xs text-[#333]"
>
<v-icon name="fa-user" fill="black" />
<p>Bu Prani</p>
</div>
</div>
</div>
<div className="mt-16">
<NotionRenderer :blockMap="article" fullPage />
</div>
</div>
</div>
</div>
</template>
<script setup>
import { ref, onMounted, computed } from "vue";
import { useRoute } from "vue-router";
import { NotionRenderer, getPageBlocks } from "vue-notion";
import { getBlogList, getBlogDetail } from "@/services/blog.service.js";
const route = useRoute();
const slug = ref(route.params.slug).value;
const blogDetail = ref();
const article = ref();
onMounted(async () => {
try {
const resultAll = await getBlogList();
blogDetail.value = resultAll.filter((blog) => blog.slug == slug)[0];
const result = await getBlogDetail(blogDetail.value.id);
article.value = result;
} catch (error) {
console.error("Error fetching blog detail:", error);
}
});
</script>
<style>
</style>
Tadaaa, artikel berhasil muncul 🎉
Namun, artikel masih berantakan karena belum ada cssnya.
Tambahkan internal css
Buat file baru di folder src/pages/blog/blog.style.css
dan copy code css dari gist berikut
https://gist.github.com/iniakunhuda/5a82306c1ab86fd8c37996abc83c8afc
Kemudian, setelah file blog.style.css
terbuat, edit bagian bawah file BlogDetail.vue
dengan kode berikut,
1
2
3
<style>
@import "@/pages/blog/blog.style.css";
</style>
Tadaaa, artikel sudahh muncul dan rapi 🎉
Sekian tutorial kali ini, next time kita akan deploy blog ini ke Netlify.