How can I build my portfolio site with $0 cost for hosting

Hau Vo Phuoc
3 min readNov 30, 2020

--

Hello, I’m Hau Vo (aka Kevin), and working as a remote freelancer for some startups in the USA. You can see my information at https://www.hauvo.me/about

Back in Oct, I would like to update my portfolio to get a new job, I have one portfolio in Google docs but I’m very lazy to update. So that’s why I would like to build an online portfolio where I can share directly with the clients and I can able to update easily via the editor. And it should cost $0 because I don’t use my portfolio site for marketing purposes.

So after thinking about some solutions, I decided to do with:

  • Ghost: as the headless CMS with a very good editor.
  • VueJS: as the front-end because I love VueJS and we can generate the static site. And we will fetch the content from the Ghost site via API.
  • Heroku: to host the Ghost CMS. It’s free too.
  • Netlify: as the front-end hosting. It’s free for static sites and we can host the VueJS site.

Install the Ghost site on Heroku

I used https://elements.heroku.com/buttons/snathjr/ghost-on-heroku to install the Ghost backend in just 5 minutes. After that, you can access the admin at http://your_ghost_site/ghost and enter the admin information.

Init the VueJS site

You can follow the guideline from the Vue CLI website to create the new project.

Add a new integration

To use the Ghost API, we need to have the Content API Key. You can get it by adding a new integration via the Ghost admin dashboard.

Load the portfolios

On the home page, I want to load my outstanding projects.

So in the ghost site, I will tag the Portfolio into the post that I would like to display on the home page.

In the VueJS site, we will load the Portfolio posts

const GhostContentAPI = require('@tryghost/content-api')// Create API instance with site credentials
const api = new GhostContentAPI({
url: process.env.GHOST_URL,
key: process.env.GHOST_CONTENT_API_KEY,
version: 'v3'
})
function getPosts() {
return api.posts
.browse({
limit: 'all',
include: 'authors,tags',
filter: 'tag:portfolio'
})
.catch((err) => {
console.error(err)
})
}

And display them

<div v-for="item in projects" :key="item.id" class="col-md-6">    <PortfolioItem :data="item" /></div>

PortfolioItem.vue

<template>
<b-link :to="ref" class="portfolio-link">
<div class="portfolio-item pl-md-5 pr-md-5">
<b-img-lazy
class="portfolio-image mb-3"
fluid
rounded
:src="image"
></b-img-lazy>
<h6 class="portfolio-item-title text-indigo">{{ name }}</h6>
<h4 class="portfolio-item-slogan">{{ slogan }}</h4>
<div class="portfolio-item-tag text-secondary">{{ tags }}</div>
</div>
</b-link>
</template>
<script>
import _ from 'lodash'

export default {
props: {
data: {
type: Object,
default: () => ({})
}
},
computed: {
ref() {
return {
path: `/project/${this.slug}`
}
},
id() {
return this.data.id
},
slug() {
return this.data.slug
},
image() {
return this.data.feature_image
},
name() {
return this.data.title
},
slogan() {
return this.data.custom_excerpt
},
tags() {
return _.get(this.data, 'tags', [])
.filter((item) => item.slug !== 'portfolio')
.map((item) => item.name)
.join(', ')
}
}
}
</script>
<style lang="scss" scoped>
.portfolio-link {
text-decoration: none;
color: black;
&:hover {
text-decoration: none;
}
}

.portfolio-item {
margin-bottom: 8rem;
text-align: left;

&:hover {
text-decoration: none;
}

.portfolio-image {
height: 300px;
width: 100%;
object-fit: cover;
}

.portfolio-item-title {
font-weight: bold;
}

.portfolio-item-tag {
font-size: 0.8rem;
font-weight: 500;
text-transform: uppercase;
}
}
</style>

As you can see, it’s very easy to build the portfolio site with VueJS and Ghost as the backend. My site is open-source and you can clone it from https://github.com/phuochau/hauvo.me. And thank you Netlify and Heroku for free hosting.

--

--

Hau Vo Phuoc

My passion is building tech products that can help millions of people in real life