Get Started Astro Tutorial — 7. Building the About page
- 1. Creating the project in Content Island
- 2. Defining the data model
- 3. Adding data
- 4. Creating the Astro project
- 5. Creating the folder structure
- 6. Connecting Content Island with Astro
- 7. Building the About page
- 8. Page with list of posts
- 9. Displaying a post
- 10. Automatic Deployment with Astro and Content Island using GitHub Actions
- 11. Deploying to Netlify
- 12. Auto-deploy with GitHub Webhooks + Content Island
7. Building the About page
Let’s build the About page. We can take two different approaches:
- One option is to first build the static HTML with Astro and then connect it to the Content Island data.
- The other is to fetch the data from Content Island, build an unstyled HTML layout, connect everything, and once it's working, add the styles.
Both approaches are valid—it depends on your preference and what you're more comfortable with. As a developer, you might prefer ensuring that the data loads correctly before handing it off to a designer to work their magic. :)
We’ll go with the second option. Developer glasses on—let’s go!
First, since we’re working with TypeScript, let’s define the model for the About page data.
From the Model tab in Content Island, we know the About model includes:
- title: Page title
- picture: Photo
- minibio: Mini bio
- bio: Extended bio
Time to turn this into code, but wait... Content Island generates this model for us—we just have to click on the TS icon in the entity name bar and copy and paste the generated interface.
The resulting code:
./src/pages/about/about.model.ts
import type { Media } from '@content-island/api-client';
export interface About {
id: string;
language: 'en';
picture: Media;
fullname: string;
shortBio: string;
extendedBio: string;
}Next, we’ll create an API function to fetch this data.
What will this function do?
- Define an async function called "getAbout" to fetch data from Content Island.
- Use Content Island’s API client "getContent", passing the content ID and content type.
- Map the returned data to our TypeScript model using the helper function "mapContentToModel".
./src/pages/about/about.api.ts
import client from "../../lib/client";
import type { About } from "./about.model";
export async function getAbout(): Promise<About> {
const aboutContent = await client.getContent<About>(
{
// TODO: IMPORTANT - PUT THE ID YOU HAVE IN YOUR INSTANCE
// Of About in Content Island
id: "685eb36e45767b0f312fce64",
contentType: "About",
}
);
return aboutContent;
}Now let’s update the About page to fetch and display the data.
First, we handle the server-side code block:
./src/pages/about/index.astro
---
import { marked } from 'marked';
import Layout from '../../layouts/Layout.astro';
import { getAbout } from './about.api.ts';
const about = await getAbout();
const aboutExtendedBio = marked(about.extendedBio || '');
---Then, the layout:
<Layout>
<div class="container about">
<div class="about__header">
<img src={about.picture.url} alt="" class="about__image" />
<div class="about__intro">
<h1>I'm, <br /> {about.fullname}</h1>
<p>{about.shortBio}</p>
</div>
</div>
<div set:html={aboutExtendedBio} class="about__bio" />
</div>
</Layout>We’ll also install a library to process Markdown content:
npm install markedAnd another library for syntax highlighting in Markdown:
npm install highlight.jsTo use it, add the CSS theme to the layout:
./src/layouts/Layout.astro
<link
rel="stylesheet"
href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.7.0/styles/github-dark.min.css"
/>Now let’s add styles to make everything look good:
<style>
.about {
display: flex;
flex-direction: column;
gap: var(--space-lg);
}
.about__header {
display: flex;
flex-direction: column;
gap: var(--space-md);
align-items: flex-end;
}
@media screen and (min-width: 768px) {
.about__header {
flex-direction: row;
}
}
.about__intro {
display: flex;
flex-direction: column;
gap: var(--space-xs);
h1 {
font-size: var(--fs-4xl);
font-family: var(--ff-title);
width: 10ch;
line-height: 1.1;
}
p {
max-width: 30ch;
font-size: var(--fs-md);
}
}
@media screen and (min-width: 768px) {
.about__intro {
padding-bottom: var(--space-md);
}
}
.about__image {
width: 100%;
object-fit: cover;
aspect-ratio: 16/9;
border-radius: var(--border-radius);
}
@media screen and (min-width: 768px) {
.about__image {
width: 50%;
aspect-ratio: 10/16;
}
}
@media screen and (min-width: 1024px) {
.about__image {
width: 30%;
}
}
.about__bio {
max-width: 800px;
margin: 0 auto;
display: flex;
flex-direction: column;
gap: var(--space-lg);
img {
width: 100%;
object-fit: cover;
aspect-ratio: 21/9;
border-radius: var(--border-radius);
}
}
</style>Nice! It’s looking good. 😎
Ready to move on to the post list?