Template structure
This section is about the file structure. You will learn how the different files are organized, but most important of all, how to manage your project with Astro. Let's dive into the folder structure.
Global structure
root
├── public/
│ ├── img/
├── src/
│ ├── js/
│ │ ├── libs/
│ │ ├── main.js
│ └── components/
│ │ ├── Component.astro
│ ├── layouts/
│ │ ├── Default.astro
│ ├── pages/
│ │ ├── index.astro
│ └── styles/
└── .npmrc
├── bulma-css-vars.config.js
├── astro.config.mjs
└── postcss.config.cjs
└── package.json
The directory structure can seem unusual for someone who is not used to build tools. We will break each part of it, so you understand how everything works. You can learn more about Astro project structures on their documentation.
Static assets
root
├── public/
│ ├── img/
Astro serves static assets like images, fonts or any type of supported files inside the /public
folder. Use this folder to store your project images and any other kind of uncompiled assets that you need in your project.
Layouts folder
root
├── src/
│ └── layouts/
Layouts are Astro components used to provide a reusable UI structure, such as a page template. We conventionally use the term Layout
for Astro components that provide common UI elements shared across pages such as headers, navigation bars, and footers. A typical Astro layout component provides Astro, Markdown or MDX pages.
But, there is nothing special about a layout component! They can accept props and import and use other components like any other Astro component. They can include UI frameworks components and client-side scripts. They do not even have to provide a full page shell, and can instead be used as partial UI templates.
Layout components are commonly placed in a src/layouts
directory in your project for organization, but this is not a requirement.
The layouts
folder holds all the template layouts. It is mandatory to have at least one layout, and it should always be named default.html
. Additional layouts can have the names you want, but don't forget the .astro
extension. Also notice the <slot />
tag that is present in each layout file. This is a reference to append the rest of the page content.
Using a layout in a page
The layouts/
folder can hold one or more layout files. But generally, you'll find one or two in there like Default.astro
. Layouts are nothing more than Astro components. Inside a page like src/pages/index.astro
, you can use a layout like this:
---
import Layout from '../layouts/Default.astro'
---
<Layout>
<!-- Your page content goes in here -->
</Layout>
You can learn more about Astro layouts by reading their documentation.
Pages folder
root
├── src/
│ └── pages/
The pages
folder holds all the template pages. Pages are focused on content. Pages are files that live in the src/pages/
sub-directory of your Astro project. They are responsible for handling routing, data loading, and overall page layout for every page in your website. You will sometimes find a particular statement at the top of Astro pages code :
---
import Layout from '../layouts/Default.astro'
import Navbar from '../components/Navbar.astro'
---
In each .astro
file, you can open a special area between a ---
and a ---
. Inside that area, everything you write is treated and processed as javascript. You can import functions, components and declare variables.
Page files
Your project pages live in this folder. They all have a .astro
extensions. Astro also support other types of files for pages. .astro
, .md
, .mdx
and .html
extensions are supported. You can learn more about Astro pages by reading their documentation.
Components folder
root
├── src/
│ └── components/
The components
folder holds all your reusable HTML components. Components are chunks of code that you want to reuse as is across your application : it can be a button, a navbar, a content section or whatever you want. Note that you can create as many sub-folders as you want to organize your components. Components are named like page files, but in Camel case : Navbar.astro
. When you want to call a component in one of your layouts
or pages
, you first need to import it:
---
import Navbar from '../components/Navbar.astro'
---
You can then use the component in your page like this:
<Layout>
<Navbar />
</Layout>
Astro components behave like components from advanced frameworks like Vue
and React
. They can instantiate and use variables, define and use props, fetch data and many more things that make Astro such a complete and solid framework. You can learn more about Astro components by reading their documentation.
Javascript
root
├── src/
│ ├── js/
│ │ ├── libs/
│ │ └── main.js
All javascript files live in the js
folder. You should be familiar with the bare bones of ES6 when working with this template. We use Alpine.js, a powerful dependency free javascript declarative framework, similar to Vue
, but with less complexity. You can learn the basics of Alpine.js by reading the documentation. Alpine works very well in a simple setup where you declare your scripts in the same HTML page.
However, since we are not importing the library from a CDN, but rather from node modules, we use a little more complex but significantly more solid setup for alpine inside an ES 6 compatible environment. In the following example, we instantiate Alpine
and load some additional plugins. The Intersect
plugin makes it easy to interact with DOM elements when they go in / out of the viewport. If you want to learn more about how Alpine and Astro work together, you can read the Astro/Alpine integration guide.
//Alpine JS and plugins import
import Alpine from 'alpinejs'
import intersect from '@alpinejs/intersect'
import persist from "@alpinejs/persist";
window.Alpine = Alpine
//Init intersect plugin
Alpine.plugin(intersect)
//Init persist plugin
Alpine.plugin(persist);
//Init Fern persisted store
Alpine.store("app", {
init() {
this.on = window.matchMedia("(prefers-color-scheme: dark)").matches;
},
isDark: Alpine.$persist(false),
});
//Start Alpine.js
Alpine.start()
In this project, rather than using the @astrojs/alpinejs
distribution, we are using a custom manual installation. With this kind of setup, you need to define the global scope importing an Alpine.js function. This is why you need to bind your function to the global window
object for Alpine to work properly. Otherwise, you'll get undefined
. Here is a practical example:
//Import the function you need from the target file
import { initLandingNavbar } from './navbar/navbar-landing';
//Bind it to the window object to access it from anywhere
window.initLandingNavbar = initLandingNavbar;
This can quickly become very verbose in your main file. Therefore, we decided to make those imports in nested index.js
barrel files. An example is src/js/libs/components/index.js
. In this file, every component JS file is imported and bound to the global window
object. Here is the sample code from that file:
//Global imports
import { initLandingNavbar } from './navbar/navbar-landing';
import { initNavbar, initNavbarLight } from './navbar/navbar';
import { initNavbarMobile } from './navbar/navbar-mobile';
import { initNavbarBottom } from './navbar/navbar-bottom';
import { initBackToTop } from './backtotop/backtotop';
//Accordions imports
import { initAccordion } from './accordion/accordion';
//Global bind to window object
window.initLandingNavbar = initLandingNavbar;
window.initNavbar = initNavbar;
window.initNavbarLight = initNavbarLight;
window.initNavbarMobile = initNavbarMobile;
window.initNavbarBottom = initNavbarBottom;
window.initBackToTop = initBackToTop;
//Accordions bind to window object
window.initAccordion = initAccordion;
Then, inside the main.js
you can easily re-import barrel files to have all your functions loaded in:
import './libs/components'
import './libs/forms'
import './libs/sections'
You know everything about how to structure your Javascript files in this project.
SCSS
root
├── src/
│ ├── styles/
│ │ ├── abstracts/
│ │ ├── base/
│ │ ├── bulma-generated/
│ │ ├── components/
│ │ ├── css-variables/
│ │ ├── forms/
│ │ ├── layout/
│ │ ├── utilities/
│ │ └── main-rtl.scss
│ │ └── main.scss
This template relies on the powerful Sass features, letting you handle complex styles in a breeze. The project relies on a modular SCSS structure. You need to import all the SCSS partials into your main SCSS stylesheets.
The main.scss
and main-rtl.scss
files centralize all the project styles. Every time you make a change in a partial file, it impacts the outputted css
files resulting from the compilation or build process. The main SCSS file starts by importing all the needed SCSS partials. Here is an example statement:
//CSS variables setup
@import "./bulma-generated/generated-vars.sass";
@import "./css-variables/all";
//Additional variables
@import './abstracts/ltr';
@import './abstracts/variables';
@import './abstracts/mixins';
//Bulma
@import "../../node_modules/bulma-css-vars/bulma-cv-lib";
//Utilities
@import "utilities/all";
//Base
@import "base/all";
//Forms
@import "forms/all";
//Layout
@import "layout/all";
//Components
@import "components/all";
Notice how partials are imported. Whereas the actual partial file name starts with an underscore and ends with a
.scss
extension, when you write imports inside yourmain.scss
file, you have to remove the underscore and the.scss
extension.
Root files
There also a few files sitting at the root of the project that we need to discuss before getting into serious business:
astro.config.mjs
: The main Astro configuration file.package.json
: Lists all your project's dependencies and gives useful metadata.postcss.config.cjs
: The main Post CSS configuration file.