Exercise 9
Add Logic with JavaScript
Separate content from presentation using JavaScript, work with static and dynamic values, and implement time-based logic.
What you'll learn
Key takeaways from this exercise
- Separating content from presentation with config files
- Learning JavaScript fundamentals: variables, data types, and functions
- Working with arrays and objects in JavaScript
- Using
.map()to transform arrays - Accessing object properties with dot notation
Introduction
Separating content from presentation
So far, you've worked with HTML for structure and components for organization. But there's a problem with the approach we've been using: the content — names, titles, tools, project descriptions — is hardcoded directly in the page files. If you want to change your name, you'd have to find every place it appears and update it individually. That's fragile and error-prone.
This is where JavaScript comes in — not for building fancy interactive features (at least not yet), but for something much more practical: separating content from presentation.
The idea is simple. Instead of writing your data directly in the HTML, you store it in a separate file — a config or data file — and then reference it from your components. Your components define how things are displayed (the structure and layout), while the data file defines what is displayed (the actual content). Change the data in one place and it updates everywhere it's used.
This pattern of separating content from presentation is fundamental in web development, and it's one of the most important things to understand when working in real codebases. It's how professional projects are organized, and it's what makes code maintainable as it grows.
Data types and variables
JavaScript has a few core data types you'll encounter constantly. A string is text, written in quotes: "Alejandro" or "Product Designer". A number is exactly what it sounds like: 30, 99.99. A boolean is either true or false — useful for on/off states. An array is a list of values: ["Cursor", "Figma", "CSS"]. And an object is a collection of key-value pairs: { name: "Alejandro", title: "Developer" }. You can think of objects as compounds made of several related values. It's very common to create arrays of objects too, for example an array of blog posts where each post has a status, content, title, and so on.
You store values in variables — named containers that hold data. In modern JavaScript, you use const for values that won't change (which is most of the time) and let for values that need to be reassigned.
TypeScript types and interfaces
As your data gets bigger, you need a way to describe what shape that data should have. That's what TypeScript is for. It doesn't create new values or change how JavaScript runs — it describes the kind of data you expect, so your editor and build tools can catch mistakes early. Think of it as an extra layer of specification on top of JavaScript that helps keep things organized, maintainable, and intentional.
TypeScript types are closely related to JavaScript data types. If a value is text in JavaScript, its type is string. If it's a number, its type is number. Arrays can be typed like string[], which means "an array of strings." Objects can also be typed, which is where an interface becomes useful.
An interface is a reusable description of an object's structure. It lists the properties an object should have and what type each property should be. For example, an interface might say that a project needs a title as a string, a year as a number, and tools as a string[].
You use interfaces to keep your data files and components aligned. If your config object is supposed to match a certain structure, you can attach an interface to it. Then TypeScript can warn you if a property is missing, misspelled, or the wrong type. In practice, that means you can write something like const project: Project = { ... } or export const SITE_CONTENT: SiteContent = { ... } to say "this object should follow this shape."
Working with data
One of the most useful things you can do with arrays is the .map() method. It takes each item in an array and transforms it into something else — for instance, turning an array of tool names into a list of <li> elements. You'll see .map() used everywhere in component code because it's the standard way to render lists of data.
Objects are accessed using dot notation: SITE_CONTENT.hero.name gets the name property from the hero object inside SITE_CONTENT. Destructuring is a shorthand for pulling multiple properties out of an object at once: const { name, title } = person extracts both values in one line.
You can also create dynamic values — values that are calculated rather than hardcoded. A simple example: instead of hardcoding "Present" as the end date for your current job, you can write a function that gets the current date and formats it automatically. Every time the site is built, it shows the right month and year without anyone updating it manually.
JavaScript in Astro
In Astro, JavaScript runs in the frontmatter section (between --- at the top of a file) during the build process. This means the JavaScript executes when the site is built, not in the user's browser. The result is pure, fast HTML — but the content is dynamic at build time. This is called a static value in the output (users see fixed content) that was generated from a dynamic value in the code (calculated when built).
The config pattern
The config pattern — centralizing your content in a data file and importing it into components — is the same pattern used in real products. Eventually, the data might come from a database or a CMS instead of a local file. But the component code stays the same — it just renders whatever data it receives. This is why separating content from presentation matters: it makes your components truly reusable and your project truly maintainable.
Create a free account to access the full course
In order to access full course content and track your progress, please sign in