Creating a Developer Portfolio with Next.js and Cosmic

Stefan Kudla's avatar

Stefan Kudla

July 19, 2022

Note: This article has been archived as it may contain features or techniques not supported with the latest version of Cosmic.

cover image

As a developer, one of the most valuable things you can do is create your presence on the internet. Your portfolio is a great place to showcase your proudest projects to the community or potential employers.

Today, we’re going to build a beautiful developer portfolio, giving you a place to store your blog posts and projects. After completing this tutorial, you will understand how building with modern developer tools comes with great benefits. Faster page loads, optimized SEO, and an intuitive developer experience will ultimately enable you to provide better products for your colleagues and clients.

Tools we’ll be using

To build our developer portfolio, we’re going to use the following technologies:

  • Next.js - A React framework that makes it easy to spin up a full-stack application.
  • Cosmic - A Headless CMS enables the independence of the data (content) layer and gives us the ability to quickly manage template content. In this case, our blog and project posts.
  • Tailwind CSS - A performant utility-first CSS framework that can be composed directly in your markup.


While the main objective of this portfolio is to be functional and showcase who you are as a developer, I wanted to ensure that we prioritized user experience and overall website performance.

  • Organized content management - With Cosmic, we can store all of our content in one place. Once we write the code for our user interface, we will not have to worry about storing content within our project. Cosmic handles all of this.
  • Next.js Image optimization - With the power of Next Image, we will ensure that our images won’t be slowing us down one bit. Storing local images of ourselves (if you prefer to include those) as well as the remote images we will query from our Cosmic bucket, we will make sure to utilize things like lazy loading, placeholder blur, and built-in image optimization from Next.js.
  • SEO and Accessibility best practices - As a web developer, it is crucial that you cater to good semantics to ensure that your site is accessible by anyone.


Install the template<br> View the live demo<br> Check out the code<br>

Starting from a blank Next.js app

To get started with this template, let’s create a new Next.js application.

Then install the dependencies.

Let’s fire up our application! After running the command below, you can open up http://localhost:3000 in your browser.

Getting started with Cosmic

First things first, let’s create a free Cosmic account. Once created, we can go ahead and create a new project. Select “Start from scratch”, then you can name your project. Since this is our primary bucket where we will be building and deploying our project, I’m going to name the bucket environment “Production”. Go ahead and select “Save Bucket”.

Creating a new Bucket in Cosmic

Next, we can start adding objects to our Cosmic Bucket.

The Content Model

The Content Model is a blueprint for our object. It consists of data that can be as simple as one single text value or as complex as storing several data values. These could be strings, numbers, booleans, etc. The whole point of configuring this content model is so that every time we create a new blog post, all of the Metafields we created in our blueprint will be there for us to fill out.

To add our custom Metafields to the content model, we can click on the plus symbol and add a new Metafield. We will be then prompted to select from a list of Metafield Types.

Cosmic content model

Categories object

Let’s create our categories object. The only piece of data we will need for our categories object is going to be the title, so we do not have to add any Metafields.

Creating Post Categories

Posts object

This is what our content model will look like for the posts object. The Metafields will be:

  • Category - Which we will link to our Post Categories object. Type: Single Object Relationship.
  • Cover Image - An image we can display at the top of our post. Type: Image / File.
  • Excerpt - A short sentence summarizing our post. Type: Plain Text Input.
  • Content - The text content that will go inside of our post. Type: Markdown.

Note that by default, when we create a new object it will have a content and slug field. We will be using the slug (which Cosmic generates for us) in our code to properly route our posts.

Post content model

Before we dive into the code, go ahead and create a post with sample data so that we can test it later on.

Works object

We will also be making a “Works” object to showcase our best projects. Copy the blueprint for the “Posts” object, though add in two additional Metafields. These will be:

  • Repo URL - A link to the projects’ GitHub repository. Type: “Plain Text Input”.
  • Live URL - A link to the live website for your project. Type: “Plain Text Input.

Works object

Installing the Cosmic NPM module

Let’s install the Cosmic dependency into our project and start writing the code that grabs the posts from our bucket.

Setting your environment variables

We will need to create three environment variables inside of a .env file in the root of our project. The Bucket Slug and Read Key can be found in your dashboard in Settings > API Access. The preview key is something that you can define yourself, so go ahead and create your own preview secret key so that you can use it later on.

Getting our Posts

Now that we have our environment variables set, we are ready to access the Cosmic API and retrieve our data.

While getting our posts, we can also create a few parameters here. In our example, getting our "Posts" and "Works" will share the same function, though we will pass in an argument when we call the function declaring which object to get. We can do this for our categories as well by passing the title of our Cosmic object as an argument.

To get a better understanding of how we are getting our data from Cosmic, let’s break it down:

  • Query - sending valid JSON queries on Object and Media endpoints. You can view a full guide here.
  • Status - if not included, status defaults to published. You can include both published and draft content by setting status to any.
  • Props - used to declare only the data you need and limit the payload size.
  • Limit - the number of objects to return.
  • Sort - sort your content.

Parsing the markdown

Since we will be writing our content in Markdown, we will need a way to serialize the markdown into HTML. To do this, we will install the dependencies remark and remark-html.

Now that we’ve installed these packages, create a file in the lib folder of your project.

Creating a list of posts

Now that we’ve done the basic setup of our Cosmic bucket, created a .env file with the required environment variables, created the functionality to get the data, and parsed our markdown, we can create a list of posts so that users can choose from them.

For our post lit, we will display the title and the excerpt from the post. This is what it will look like:

Post List

We can create a “PostList.jsx” component so that we can reuse our post list with ease on several parts of our site. When we render this component on one of our pages, we will pass the data of all the posts we receive from Cosmic to the “allPosts” parameter.

Rendering the Post List

Now, let’s take that post list and render it on our “Posts” page. If you haven’t yet, create a folder in your “pages” folder within your directory called “posts”. Then, let’s create the index file for that page where our PostList will live.

With getStaticProps, we will call the functions we created earlier to get those posts from Cosmic. The awesome part about this is that when it comes to build time, these posts will be built statically and deployed to an edge CDN, making the pages available to users around the world within milliseconds.

We are also calling our function that gets the post categories so that we can then display them on the page and create the functionality to filter through the posts.

Creating an individual post page

In the “posts” folder (pages/posts), let’s create a [slug].jsx file. This is where we can write the code for our individual posts.

The content for our post page will be composed of three components:

  • PostHeader - Containing our PostTitle, post metadata (date and category), and cover image.
  • PostTitle - The title of the post
  • PostContent - The styled HTML that we converted from Markdown.
  • markdown-styles.module.css - A stylesheet for our markdown

The page itself:

Now that we’ve implemented the code into our [slug].jsx page, we can click on any of the posts from the Post List and view the content of our post.

Blog post page

Creating the Work list and Work page

Now that we have the structure for our post page and post index page, we can repeat this for our work page. We can create a “works” folder in our “pages” folder, followed by an index.jsx and [slug].jsx.

Copy the code from both the index.jsx and [slug].jsx in pages/posts, and simply change the instances of “post(s)” to “work(s)”.

Using Preview Mode

With Next.js and Cosmic, we can view drafts of our posts before they are published. In Cosmic, create a post, and once you have filled out the Metafields, select “Save Draft” rather than “Publish”.

Testing preview mode

Before we can preview our post, let’s set our app up so that we can utilize this functionality.

  1. Grab the COSMIC_PREVIEW_SECRET you created earlier. Then click on the settings icon on your object in Cosmic.

Selecting the object settings in the dashboard

  1. Scroll down to the “Preview Link” field. Replace the <randompreviewsecret> with your own COSMIC_PREVIEW_SECRET. What we are doing here is telling our application to go to this route if the post has a status of “draft”.

    Note that our link is set to local host and preview mode will only work when we are running our local development server. Once your app is deployed, you can replace “http://localhost:3000” with your domain name.

Setting the preview mode query

  1. Let’s go back to our cosmic.js file and create a function that gets the preview post from Cosmic.

  1. Now let’s create two API routes in our project - one for the preview itself and the other for exiting the preview. Fortunately, Next.js handles API routes out of the box.

  1. Now we can go back to our post in Cosmic, and select the “preview” button and our application will open up the preview of our post.

Selecting the preview button on a post

  1. Before we’re done with our preview mode, we need to create a component that alerts us if we are in preview mode, with a link to exit the preview mode. This link takes us to that “exit-preview.js” API route we created above.

Preview mode banner

  1. Now that we have our banner made, all we need to do is import it into our [slug].jsx pages. By default, our Cosmic object comes with a “status” key value pair. If our post is not published, it has a status of “draft”.

Deploying to Vercel

To deploy your project to Vercel, click here. This link will automatically clone the template into a new repository and build and deploy your new application (how cool!). All you have to do is provide the environment variables from earlier.


Now you have a fully functional developer portfolio that you can use to showcase your projects and share your blog posts with the tech community. I hope you enjoyed this tutorial, and if you have any feedback or questions, feel free to join us over at the Cosmic Slack Channel.

Back to blog