Skip to content

Project Overview

When you have your project running locally as described in the Getting Started guide, it’s time do see how you can customize it to become truly yours. This page will guide you through the basics you need to become productive with Zenith. For more advanced topics, check out our Guides.

Data flow

In Zenith your data is clearly separated from its presentation. This way you can enter the data once and then use it to create multiple variants of your resume. A typical data flow in Zenith looks like the following:

  1. You operate on files located in the src/content directory to specify the data.
  2. You access the data within .astro components in the src/pages directory.

Let’s take a closer look at how it works.

1. Specifying the data

In your IDE go to the src/content/projects/moni-buddy.mdx file with an example project. Its content is separated into frontmatter (between two ---) and the body (after both ---). The frontmatter contains project’s data that must follow some schema and is written in YAML. The body contains project’s description written in Markdown and can take any form. You can also utilize MDX features there.

---
name: 'MoniBuddy'
description: 'Open source personal budget management app'
image: './moni-buddy.png'
# other fields...
---
This personal project is a web application designed to...
2. Using the data

Components that displays UI based on our data are located in src/pages directory. As an example, let’s take a look at the src/pages/index.astro file that contains page displayed under the localhost:4321 route. You can use Ctrl/Cmd + F to find the “MoniBuddy” project both on the website and in the file.

<Section class="gap-8">
<SectionTitle>Projects</SectionTitle>
<VerticalList class="gap-10">
<Project entry={getEntry('projects', 'moni-buddy')} />
<Separator />
<Project entry={getEntry('projects', 'user-board')} />
</VerticalList>
</Section>

As you can see, we access project data using getEntry() function, that takes two arguments: collection name (name of the folder within src/content) and collection entry slug (name of the file). The result is passed to the <Project /> component that’s responsible for rendering the project card.

Astro 101

Zenith is built on top of Astro. To work effectively you don’t need extensive Astro knowledge besides a few basic concepts presented below.

Components

Astro components are files with the .astro extension that contain HTML-like syntax mixed with JavaScript. A component may contain the following:

lucky-number.astro
---
/* JavaScript (server-side) */
import { random } from 'lodash';
const luckyNumber = random(100);
---
<!-- HTML -->
<p>Your lucky number is {luckyNumber}!</p>
<!-- CSS applied only to this component -->
<style>p { color: red; }</style>
<!-- CSS applied globally -->
<style is:global>body { background: lightgray; }</style>
<!-- JavaScript (client-side) -->
<script>window.alert('Get your lucky number!');</script>

You can import one Astro component into another using the default import. Components can also specify expected properties using a TypeScript type named Props.

sum.astro
---
interface Props { a: number; b: number; }
const { a, b } = Astro.props;
---
<p>{a} + {b} = {a + b}</p>
index.astro
---
import Sum from './sum.astro';
---
<Sum a={2} b={3} />

To learn more about Astro components, visit the official documentation.

Pages

Astro pages are components located in the src/pages directory. Each of them represents a single route of the application. For example:

To learn more about Astro pages, visit the official documentation.

Page structure

When exploring resume routes you may discover that localhost:4321 and localhost:4321/template look exactly the same. The only difference between those two is the approach we used to create them.

Component approach

You import all components at the top of the file and use them to build the interface. To access data you utilize the getEntry() function. src/pages/index.astro uses this approach.

---
// Import components and utilities.
import { getEntry } from 'astro:content';
import { VerticalList } from '@/components/list';
import Separator from '@/components/separator.astro';
import Project from '@/web/components/project.astro';
import Layout from '@/web/components/layout.astro';
import { Section, SectionTitle } from '@/web/components/section';
---
<!-- Use the components to build the interface -->
<Layout>
<Section class="gap-8">
<SectionTitle>Projects</SectionTitle>
<VerticalList class="gap-10">
<Project entry={getEntry('projects', 'moni-buddy')} />
<Separator />
<Project entry={getEntry('projects', 'user-board')} />
</VerticalList>
</Section>
</Layout>

Component approach gives you full control over the interface. You can customize classes and add new components on each level of the hierarchy. It’s also easy to debug as you can see all components and easily adjust their styles. However, it requires more boilerplate code and may be harder to maintain if you want to create multiple pages sharing the same UI.

Template approach

You import a single template component and pass the data to it. The template component hides implementation details of rendering the interface. src/pages/template.astro uses this approach.

---
// Import the template component.
import type { ComponentProps } from 'astro/types';
import MainTemplate from '@/web/templates/main-template.astro';
// Specify the data.
const props: ComponentProps<typeof MainTemplate> = {
sections: [
{
collection: 'projects', // Name of the collection to use.
title: 'Projects',
entries: ['moni-buddy', 'user-board'], // Collection entry slugs.
},
],
};
---
<!-- Pass props to the template component -->
<MainTemplate {...props} />

Template approach shines when you want to create multiple pages with the same UI, but different data. It guarantees UI consistency and is easier to grasp (if you don’t plan to modify the <MainTemplate /> we provide). However, it’s consistency-focused approach makes it hard to customize the interface on a granular level. It’s also harder to debug as there is additional template logic responsible for rendering the page.

How to choose one?

  • If you choose the component approach, delete both template.astro files in the src/pages directory. Alternatively you can rename them to _template.astro so they won’t be builded.
  • If you choose the template approach, delete both index.astro files in the src/pages directory. Alternatively you can rename them to _index.astro so they won’t be builded. You need to also rename existing template.astro files to index.astro