Learning Astro
What is Astro?
-
Astro is a modern web framework for building fast, content-focused websites. It is particularly good for blogs, documentation sites, portfolios, marketing sites, landing pages, and other sites where most of the page is content rather than highly interactive application UI.
-
Astro is a web framework, similar in category to - Next.js, Nuxt, SvelteKit, Remix, Gatsby, Jekyll, etc.; but with a different approach.
Traditional Frameworks Astro Framework Browser
↓
JavaScript application
↓
Render page
↓
User sees contentAstro
↓
Build HTML
↓
Browser Receives HTML
↓
User Immediately sees content -
JavaScript is added only where you actually need interactivity.
Why Use Astro?
-
Modern websites often ship enormous amounts of JavaScript. Imagine a blog page containing the elements where there’s almost no interactivity, yet traditional React application could potentially send JavaScript for something that doesn’t need JavaScript. Instead, Astro renders most of the page into plain HTML and CSS.
-
Astro follows Islands Architecture: In the below diagram, Astro calls that interactive portion an
island. Most of the page doesn’t need JavaScript, only the comments widget can be using JavaScript if required.┌───────────────────────────────┐ │ Navbar │ ├───────────────────────────────┤ │ Blog Article │ │ Lots of Text │ │ [Interactive Comments Widget] │ │ More Article Text │ └───────────────────────────────┘ -
Astro is not limited to its own component system. You can use Astro components, React, Vue, Svelte, Preact, Solid, or other framework integrations. It is an all-in-one web framework. It includes everything you need to create a website, built-in.
-
Astro has first-class Markdown support. For a blog, you can keep almost all of your article content in separate
.mdfiles and let Astro handle routing, layouts, HTML generation, RSS, SEO, etc. -
Astro also allows Markdown files to contain Frontmatter alongside their content. Frontmatter is a block of structured metadata placed at the absolute beginning of a markdown file that can be used to display blog information without maintaining a separate database.
--- title: "What is Astro?" description: "A beginner's guide to Astro." pubDate: 2026-08-15 author: "Rishabh" tags: - Astro - Web Development --- # What is Astro? Your article starts here... -
Astro supports both
.mdand.mdxfiles side-by-side, though .mdx requires adding an official integration. It also supports extended Markdown syntax natively.
MDX is an extension of standard Markdown that lets you write JSX, use JavaScript expressions, and import UI components directly inside your documents.
Essential Prerequisites for Learning Astro
-
HTML
- Basic HTML attributes.
- Headings, paragraphs, links, images & forms.
- Semantic elements (header, nav, main, section, article, etc.)
-
CSS
- Selectors, Box model, Flexbox & Grid.
- Responsive design.
- Basic positioning.
-
JavaScript
- Variables, functions, arrays, objects, if/else & loops.
- DOM basics & ES6 syntax.
- Promises and async/await — useful later, but not required to start.
-
Node.js + npm
- Know what Node.js is.
- Installing packages.
npm install,npm run dev,npm run build
-
Basic command line
- Creating files/folders.
cd,dir / ls
-
Basic Git (optional)
- Working with Git.
git init,git add,git commit,git push
Setting Up Required Dev Tools
-
Node.js: Astro supports even-numbered Node.js versions. The current minimum supported version is
v22.12.0. Odd-numbered versions likev23are not supported.To check to see whether you already have a compatible version installed you can use following command in your terminal:
node -v # v24.19.0 npm -v # 11.17.0 -
For MDX support install Astro’s official MDX integration using below command:
npx astro add mdx -
Code Editor: VS Code is an excellent choice for Astro and is the officially recommended text editor by the
Official Astro Documentation. You can use below mentioned VS Code extensions to make your coding fast and error free.- Astro - Official Astro language support
- Prettier - Code formatter
- Markdown All in One - Useful if you’re writing lots of Markdown
-
Git Setup: Follow this quick guide to install and setup git
Setting up Git.
Creating Astro Project
-
Run the following command using your preferred package manager.
npm create astro@latest // npm # or pnpm create astro@latest // pnpm # or yarn create astro // yarn -
Enter
yto installcreate-astro. -
When the prompt asks you where to create the project, type in the name of a folder to create a new directory for your project, e.g.
./my_blogs -
You will see a short list of starter templates to choose from. Use the arrow keys (up and down) to navigate to the minimal (empty) template, and then press return (enter) to submit your choice.
astro Launch sequence initiated. dir Where should we create your new project? ./my_blogs tmpl How would you like to start your new project? — A basic, helpful starter project (recommended) — Use blog template — Use docs (Starlight) template > Use minimal (empty) template -
When the prompt asks you whether or not to install dependencies, enter
y. -
When the prompt asks you whether or not to initialize a new git repository, enter
y. -
When the install wizard is complete, you no longer need this terminal. You can now open VS Code to continue.
Running Project In Dev Mode
-
In VS Code open the Astro project folder that you created during the setup earlier and setup required extensions will provide syntax highlighting and autocompletions for your Astro code.
-
Toggle the visibility of terminal inside VS Code using
Ctrl + JorCmd ⌘ + Jin macOS. -
Start the Astro local dev server by running the below command into VS Code’s terminal:
npm run dev // npm # or pnpm run dev // pnpm # or yarn run dev // yarn -
Astro starts a local development server at
http://localhost:4321/. Enter this into the browser to see a live preview of your new Astro website!Here’s what the Astro “Empty Project” starter website should look like in the browser preview.

-
Keep the server running in the VS Code terminal and hide it by toggling its visibility.
Understanding Astro Project Structure
-
Overall Structure
The project currently looks like this:
my_blogs/ # Project root directory. │ ├── .astro/ # These are are Astro generated internal files/dirs containing │ ├── collections/ # dev data, project settings, content collection information, │ ├── dev.json # and TypeScript type declarations used by Astro and editor, │ ├── settings.json # so you generally don’t edit or manage them manually. │ └── types.d.ts │ ├── .vscode/ # VS Code project-specific configuration directory │ ├── extensions.json # containing settings that can be shared with anyone │ └── launch.json # who opens the project in VS Code. │ ├── node_modules/ │ ├── public/ │ ├── favicon.ico │ └── favicon.svg │ ├── src/ │ └── pages/ │ └── index.astro │ ├── .gitignore # Tells Git which files and folders not to track or commit. ├── AGENTS.md # Instruction file intended for AI coding agents or dev tools. ├── CLAUDE.md # Instruction file for Claude/AI coding agents. ├── astro.config.mjs ├── package-lock.json ├── package.json ├── README.md # This is project documentation written in markdown. └── tsconfig.json # The configuration file for TypeScript, defining # how TypeScript should check, interpret, and # compile your project's .ts and .tsx files. -
node_modules/-
This is the folder where
npmstores all the actual JavaScript/Node.js packages installed for your project, such as Astro, Vite, and TypeScript, along with their own dependencies. -
When you run npm install,
npmreads the dependencies listed inpackage.jsonand downloads them intonode_modules/. -
You normally should never edit this folder manually; if it becomes corrupted or causes problems, you can delete it and run
npm installagain to recreate it. -
Since
node_modules/can be very large and can always be regenerated frompackage.jsonand the lock file, it is normally not committed to Git and is therefore included in.gitignore.
-
-
public/- This is an Astro directory used for static assets that should be copied directly to the final website without being processed.
- It can contain files such as images, PDFs, favicon.svg, hello.txt, and downloadable documents.
- The folder structure is preserved in the generated website; for example,
public/hello.txtbecomes accessible at/hello.txt.
-
src/-
This is the main directory where your Astro website’s source code and content live. Most of your development work will happen inside this directory.
-
It initially contains files such as
src/pages/index.astro, but as the project grows, it can include directories likecomponents/,content/,layouts/,pages/,styles/, andassets/.
-
-
src/pages- This is one of the important directories in Astro because it controls your website’s routing through file-based routing. In simple terms, the files you create inside
src/pages/determine the URLs of your website. - For example,
index.astrobecomes/(the homepage),about.astrobecomes/about, andcontact.astrobecomes /contact. Thus, each page file generally corresponds to a route on your website.
- This is one of the important directories in Astro because it controls your website’s routing through file-based routing. In simple terms, the files you create inside
-
Understanding
.astroFile-
The
.astroextension means this is an Astro component/page. -
This file has two main parts: Frontmatter and the Template section.
-
The frontmatter is the section at the top of the file, enclosed between two sets of
---. It contains JavaScript/TypeScript code that runs during the Astro build/rendering process. You can use it to define variables, import components, fetch data, process content, and prepare everything your page needs. -
Everything after the closing
---is the template. This section looks similar to HTML, but Astro also allows you to use expressions, components, and Astro-specific syntax. It defines what gets rendered on the webpage.
-
-
astro.config.mjs-
It is one of the important configuration files in an Astro project. It tells Astro how the project should behave and how it should be built or run.
-
The .mjs extension indicates that it is a JavaScript ES module. This file can configure things such as integrations, site URL, base path, build behavior, server settings, adapters, Markdown, redirects, experimental features, and image handling.
-
-
package.json-
It is one of the important files in a Node/Astro project because it describes the project and defines its dependencies, development dependencies, project metadata, and npm scripts.
-
The
scriptssection inside it is especially important because it defines commands that can be run withnpm run. For example,npm run devexecutesastro devto start the development server,npm run buildexecutesastro buildto create the production build, andnpm run previewexecutesastro previewto preview that build.
-
-
package-lock.json-
It is an npm-generated file that records the exact versions and dependency tree resolved for your project. While
package.jsonspecifies the dependencies your project needs,package-lock.jsonrecords the specific versions actually resolved by npm, making installations more consistent and reproducible across different environments. -
You generally should not edit it manually; npm updates it automatically when necessary, such as when running
npm install.
-
Creating GitHub Repository
-
Use GitHub to store your repository and connect it to a web host to deploy it live on the internet. Feel free to use any alternative online Git provider if you prefer.
-
Before creating the GitHub remote repository, let’s make an initial commit by making the following change to the
src/pages/index.astrofile.<body>
<h1>Astro</h1>
<h1>My Astro Blog</h1>
</body>Then commit the changes using the following commands:
cd my_blogs git add . git commit -m "Initial commit" -
Option A: Create a remote repository on GitHub with the same name as your local repository, then link and push your code to it.
git remote add origin https://github.com/<USERNAME>/my_blogs.git git branch -M main git push -u origin main -
Option B: If you have a local repository and the corresponding remote repository does not yet exist, you can push the repository to GitHub, creating the remote repository in the process.
For a public repository:git branch -M main gh repo create my_blogs --source=. --public --pushFor a private repository:
git branch -M main gh repo create my_blogs --source=. --private --pushIf you encounter an error such as gh: command not found, install the GitHub CLI using the following command:
sudo apt install ghand authorize using the command:gh auth login.
Deploy Project
-
You can use various providers to deploy your website. We’ll be using Cloudflare Pages. Follow these steps to
set up a Cloudflare Pageand use the configuration options below when connecting it to your GitHub repository.Framework preset:
Astro
Build command:npm run build
Build output directory:dist
Pages
-
Adding Pages (.astro files) in
src/pages/. -
Adding Posts (.md files) in
src/pages/posts. -
Adding JS elements in page’s frontmatter and using them in template section.
-
Adding stylesheet
src/styles/global.css.
Components
-
Astro components, usually defined inside
src/components/allow us to reuse code for common elements across your website. For example:- A Navigation component that presents a menu of links to your pages.
- A Footer component to include at the bottom of each page.
- A Social Media component, used in the Footer, that links to profile pages.
- An interactive Menu component to toggle the Navigation on mobile
-
The directory
src/components/holds .astro files that will generate HTML but that will not become new pages on your website. -
Create a new file:
src/components/Navigation.astroadd navigation links for each of the pages. For example:--- --- <a href="/">Home</a> <a href="/about/">About</a> <a href="/blog/">Blogs</a>- Import your new component into the frontmatter section of all the pages:
import Navigation from '../components/Navigation.astro';- Add the new navigation component to the template section of all the pages:
<Navigation /> -
Similarly, you can add other common components like
Footer.astro,Social.astro,Header.astro,Menu.astro, etc. -
JavaScript Components: We can use a directory like
src/scripts/to store common JavaScript scripts that can be imported and used wherever required.
Any JavaScript written in a component’s frontmatter is executed at build time only and is not sent to the browser.
This is better because it makes the important distinction clear: the frontmatter code does not become client-side JavaScript.
Optionally, Astro can send JavaScript to the browser to add client-side interactivity to components.
Client-side JavaScript is sent to the user’s browser when it is included in a component’s<script>tag or loaded as an external script.
Layouts
-
A layout is a reusable component that defines the common structure shared by multiple pages.
-
They are usually stored in the
src/layouts/directory. -
<slot />: This is a special Astro element that acts as a placeholder for content passed into a component.-
For example, we can define a layout component
src/layouts/BaseLayout.astro. -
This layout can then be used by different pages like this:
--- import BaseLayout from "../layouts/BaseLayout.astro"; const pageTitle = "Home Page"; --- <BaseLayout> <h2>My Blog's Subtitle</h2> <!--content between its opening and closing tags--> </BaseLayout>- We can place
<slot />anywhere inBaseLayout.astrowhere we want the content passed to the component to be displayed. - Astro renders the content passed between the component’s opening and closing tags at the location of
. This allows components, especially layouts, to define a reusable structure while letting each page or component provide its own content.
The
<slot />allows you to inject (or “slot in”) child content written between opening and closing<Component> ... </Component>tags to anyComponent.astrofile. -
-
Astro.props: Props (short for properties) are values passed from a parent component to a child component. They allow a component to receive data from the component that uses it, making the component reusable with different data.
A parent component or page can pass one or more props to a component:<BaseLayout pageTitle={pageTitle}> <h2>My Blog's Subtitle</h2> </BaseLayout> <!-- or --> <BaseLayout pageTitle={pageTitle} description="A technical blog." > <h2>My Blog's Subtitle</h2> </BaseLayout>The component (the layout in this case) can access the passed props through
Astro.props:--- const { title } = Astro.props; ---Or, when multiple props are passed:
--- const { title, description } = Astro.props; --- -
Astro automatically treats the Markdown content as the slot content when the Markdown file uses that layout: When using a common layout for Markdown posts (
.mdfiles), you don’t need to wrap the Markdown content in component tags to make it available to<slot />. Astro automatically passes the rendered Markdown content to the layout’s<slot />.
For example, create a new layout atsrc/layouts/MarkdownPostLayout.astro:--- const { frontmatter } = Astro.props; --- <meta charset="utf-8" /> <h1>{frontmatter.title}</h1> <p>{frontmatter.pubDate.toString().slice(0,10)}</p> <p><em>{frontmatter.description}</em></p> <p>Written by: {frontmatter.author}</p> <slot />You can then apply this layout to a Markdown post, such as
src/pages/posts/post-1.md, using the layout property in the frontmatter:--- layout: ../../layouts/MarkdownPostLayout.astro title: 'My First Blog Post' pubDate: 2026-07-01 description: 'This is the first post of my new Astro blog.' author: 'Astro Learner' --- ## Welcome to my _new blog_ about ...Here, the Markdown content (
## Welcome...) is automatically rendered and passed toin MarkdownPostLayout.astro -
Nested layouts: You can reuse an existing layout inside another layout to combine their functionality. Since you already have a ‘BaseLayout.astro’ that defines the overall layout of your pages, you can nest it inside ‘MarkdownPostLayout.astro’. ‘MarkdownPostLayout.astro’ provides additional templating for common blog post properties such as title and date, while ‘BaseLayout.astro’ ensures that your blog posts share the same overall look and feel as the rest of your site.
To do this, importBaseLayout.astroinsrc/layouts/MarkdownPostLayout.astroand use it to wrap the entire template content.--- import BaseLayout from "./BaseLayout.astro"; const { frontmatter } = Astro.props; --- <BaseLayout pageTitle={frontmatter.title}> <p>{frontmatter.pubDate.toString().slice(0, 10)}</p> <p><em>{frontmatter.description}</em></p> <p>Written by: {frontmatter.author}</p> <img src={frontmatter.image.url} width="300" alt={frontmatter.image.alt} /> <hr /> <slot /> </BaseLayout>Here, we have removed the
<h1>and<meta>tags from this file because they are already handled byBaseLayout.astro.
Astro APIs
-
import.meta.glob(): It allows you to import multiple files from your local filesystem at once using wildcard patterns. It scans your project directory for files matching the specified pattern and returns them as an object, where the object keys are the file paths. It is particularly useful for working with Markdown/MDX blog posts, images, JSON files, and other files that need to be discovered dynamically.
For example, suppose you have a page that links to all the posts in thesrc/pages/postsdirectory:<ul> <li><a href="/posts/post-1/">Post 1</a></li> <li><a href="/posts/post-2/">Post 2</a></li> <li><a href="/posts/post-3/">Post 3</a></li> </ul>Instead of manually adding each post to the list, we can generate the entire list dynamically using
import.meta.glob():--- const allPosts = Object.values( import.meta.glob('./posts/*.md', { eager: true }), ); --- <ul> {allPosts.map((post: any) => ( <li> <a href={post.url}>{post.frontmatter.title}</a> </li> ))} </ul>Here,
./posts/*.md matches all Markdown files in thepostsdirectory, while{ eager: true }tells Astro to import all matching files immediately. We then useObject.values()to get the imported files as an array, allowing us to iterate over them with.map(). -
Dynamic Routes: Files with square brackets in their names (like
[slug].astro) are used to create dynamic routes. They let one template generate many pages based on changing URL parameters or data. Square brackets indicate a dynamic route parameter.
Now suppose you have:src/pages/tags/[tag].astro[tag]is not treated as the literal word tag. Instead, Astro treats it as a placeholder. So the same file can represent:/tags/astro/ /tags/blogging/ /tags/successes/ /tags/community/The value inside [tag] changes. Think of it like:
/tags/[tag]/ ↑ variableDynamic Parameters extracted from the current URL is stored in
Astro.paramsobject. In the above example, if you visithost_url/tags/astro,Astro.paramswill essentially contain:{ tag: "astro" }The tag parameter can be accessed as:
const tag = Astro.params.tag; // or const { tag } = Astro.params;Astro also supports Rest Parameters, which allow a single file to match URL paths of any depth, and Multiple Dynamic Parameters, which allow a single file to capture multiple variables from a URL simultaneously.
-
getStaticPaths(): This function tells a dynamic page (like[tag].astro) which URL paths to pre-render into static HTML files during the build process. It is required for dynamic routes when your site is in static generation mode.
Here, we have defined 5 static pages insrc/pages/tags/[tag].astrothat Astro will generate:export async function getStaticPaths() { return [ { params: { tag: "astro" } }, // host_url/tags/astro { params: { tag: "successes" } }, // host_url/tags/successes { params: { tag: "community" } }, // host_url/tags/community { params: { tag: "blogging" } }, // host_url/tags/blogging { params: { tag: "setbacks" } }, // host_url/tags/setbacks ]; } const { tag } = Astro.params;Astro calls
getStaticPaths()function internally during the build process. It uses the returned array to determine which routes to generate. Each generated page uses the same template defined insrc/pages/tags/[tag].astro, with the correspondingtagvalue available throughAstro.params.tag. -
Using
propsin dynamic routes: You can add followingpropsto yourgetStaticPaths()function in order to make data from all your blog posts available to each page route:export async function getStaticPaths() { const allPosts = Object.values( import.meta.glob("../posts/*.md", { eager: true }), ); return [ { params: { tag: "astro"}, props: {posts: allPosts}}, { params: { tag: "successes"}, props: {posts: allPosts}}, { params: { tag: "community"}, props: {posts: allPosts}}, { params: { tag: "blogging"}, props: {posts: allPosts}}, { params: { tag: "setbacks"}, props: {posts: allPosts}}, ]; } const { tag } = Astro.params; const { posts } = Astro.props; const filteredPosts = posts.filter((post: any) => post.frontmatter.tags?.includes(tag));And now, this information can be used in various ways just like here we are using it to display links to all the posts containing the selected tag:
<h2>Posts tagged with {tag}</h2> <ul> { filteredPosts.map((post: any) => ( <li> <a href={post.url}>{post.frontmatter.title}</a> </li> )) } </ul>Each post can define its attributes(metadata) in the frontmatter, for example:
--- title: "My First Blog Post" tags: ["successes"] ---If you need information to construct the page routes, write it inside
getStaticPaths().
To receive information in the HTML template of a page route, write it outsidegetStaticPaths().Also, we can create an array containing all the existing tags used across all your posts using Astro’s built-in TypeScript support:
export async function getStaticPaths() { const allPosts = Object.values(import.meta.glob('../posts/*.md', { eager: true })); const uniqueTags = [...new Set(allPosts.map((post: any) => post.frontmatter.tags).flat())]; return uniqueTags.map((tag) => { const filteredPosts = allPosts.filter((post: any) => post.frontmatter.tags.includes(tag)); return { params: { tag }, props: { posts: filteredPosts }, }; }); } const { tag } = Astro.params; const { posts } = Astro.props; const filteredPosts = posts.filter((post: any) => post.frontmatter.tags?.includes(tag));
Adding RSS Feed
-
An RSS Feed (Really Simple Syndication) is a standardized way for websites to publish updates that people can subscribe to. It generates a non-HTML document with information about all of your blog posts that can be read by Feed Readers. This document is updated every time your site is rebuilt.
-
Astro provides an official integration package that makes it easy to add an RSS feed to your website. Install it using:
npm install @astrojs/rss
- Create a new file in
src/pages/calledrss.xml.jsand add the following code:
import rss, { pagesGlobToRssItems } from '@astrojs/rss';
export async function GET(context) {
return rss({
title: 'Astro Learner | Blog',
description: 'My journey learning Astro',
site: context.site,
items: await pagesGlobToRssItems(import.meta.glob('./**/*.md')),
customData: `<language>en-us</language>`,
});
}
- Add the
siteproperty to the Astro configuration with your website’s unique URL:
import { defineConfig } from "astro/config";
export default defineConfig({
site: "https://example.com"
});
- You can now access your RSS feed at
http://localhost:4321/rss.xml. It will display the raw, unformatted XML containing an item for each of your.mdfiles.
Astro Islands
-
Astro helped pioneer and popularize a frontend architecture pattern called Islands Architecture.
-
Islands Architecture works by rendering the majority of a page as fast, static HTML, while adding smaller “islands” of JavaScript only where interactivity or personalization is needed—for example, an image carousel, search bar, or interactive form.
-
This approach avoids the large, monolithic JavaScript payloads that can slow down the responsiveness of many modern JavaScript web frameworks.
-
Astro can use Preact, a lightweight JavaScript library for building interactive user interfaces, to create these interactive islands.
-
You can add Preact to an Astro project with the following command:
npx astro add preact
-
Creating a Preact Greeting Banner: This component will take an array of greeting messages as a prop and show the first as a welcome message. The user can click a button to get a new random message.
- Create a new file in
src/components/namedGreeting.jsxand add following code:
import { useState } from 'preact/hooks'; export default function Greeting({messages}) { const randomMessage = () => messages[(Math.floor(Math.random() * messages.length))]; const [greeting, setGreeting] = useState(messages[0]); return ( <div> <h3>{greeting}! Thank you for visiting!</h3> <button onClick={() => setGreeting(randomMessage())}> New Greeting </button> </div> ); }- Import and use this component on your Home page
index.astro:
--- import Greeting from '../components/Greeting'; const pageTitle = "Home Page"; --- <h1>{pageTitle}</h1> <Greeting client:load messages={["Hi", "Hello", "Howdy", "Hey there"]} />- The
client:loaddirective tells Astro to load and execute the component’s JavaScript on the client as soon as the page loads, making the component interactive. This process hydrates the component, making it a hydrated component.
Hydrated Component: A component whose already-rendered HTML has been connected to its client-side JavaScript logic and is therefore interactive.
Hydration: The process of attaching client-side JavaScript behavior to already-rendered HTML so that the component becomes interactive.
- Create a new file in
-
Back on Dry Land: You can go pretty far with just vanilla JavaScript and CSS!
-
Let’s build a clickable icon to let your users toggle between light or dark mode using another
<script>tag for interactivity without sending any framework JavaScript to the browser. -
Create a new file at
src/components/ThemeIcon.astroand paste the following code into it:
--- --- <button id="themeToggle" aria-label="Toggle theme"> <svg aria-hidden="true" width="30px" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"> <path class="sun" fill-rule="evenodd" d="M12 17.5a5.5 5.5 0 1 0 0-11 5.5 5.5 0 0 0 0 11zm0 1.5a7 7 0 1 0 0-14 7 7 0 0 0 0 14zm12-7a.8.8 0 0 1-.8.8h-2.4a.8.8 0 0 1 0-1.6h2.4a.8.8 0 0 1 .8.8zM4 12a.8.8 0 0 1-.8.8H.8a.8.8 0 0 1 0-1.6h2.5a.8.8 0 0 1 .8.8zm16.5-8.5a.8.8 0 0 1 0 1l-1.8 1.8a.8.8 0 0 1-1-1l1.7-1.8a.8.8 0 0 1 1 0zM6.3 17.7a.8.8 0 0 1 0 1l-1.7 1.8a.8.8 0 1 1-1-1l1.7-1.8a.8.8 0 0 1 1 0zM12 0a.8.8 0 0 1 .8.8v2.5a.8.8 0 0 1-1.6 0V.8A.8.8 0 0 1 12 0zm0 20a.8.8 0 0 1 .8.8v2.4a.8.8 0 0 1-1.6 0v-2.4a.8.8 0 0 1 .8-.8zM3.5 3.5a.8.8 0 0 1 1 0l1.8 1.8a.8.8 0 1 1-1 1L3.5 4.6a.8.8 0 0 1 0-1zm14.2 14.2a.8.8 0 0 1 1 0l1.8 1.7a.8.8 0 0 1-1 1l-1.8-1.7a.8.8 0 0 1 0-1z"/> <path class="moon" fill-rule="evenodd" d="M16.5 6A10.5 10.5 0 0 1 4.7 16.4 8.5 8.5 0 1 0 16.4 4.7l.1 1.3zm-1.7-2a9 9 0 0 1 .2 2 9 9 0 0 1-11 8.8 9.4 9.4 0 0 1-.8-.3c-.4 0-.8.3-.7.7a10 10 0 0 0 .3.8 10 10 0 0 0 9.2 6 10 10 0 0 0 4-19.2 9.7 9.7 0 0 0-.9-.3c-.3-.1-.7.3-.6.7a9 9 0 0 1 .3.8z"/> </svg> </button> <style> #themeToggle { border: 0; background: none; } .sun { fill: black; } .moon { fill: transparent; } :global(.dark) .sun { fill: transparent; } :global(.dark) .moon { fill: white; } </style>- Import and add the
<ThemeIcon />component toindex.astroas shown below:
--- ... const pageTitle = "Home Page"; import ThemeIcon from "../components/ThemeIcon.astro"; --- <ThemeIcon /> <h1>{pageTitle}</h1> ...- Open
global.cssand define the styles for dark mode:
html.dark { background-color: #0d0950; color: #fff; } .dark .menu { background-color: #fff; color: #000; } .dark .nav-links a:hover, .dark .nav-links a:focus { color: #0d0950; } .dark .nav-links a { color: #fff; } .dark a { color: #ff9776; }- Finally, add interactivity to an Astro component, use a
<script>tagsrc/components/ThemeIcon.astroafter your<style>tag:
... :global(.dark) .sun { fill: transparent; } :global(.dark) .moon { fill: white; } </style> <script is:inline> window.localStorage.setItem("theme", "light"); const handleToggleClick = () => { const element = document.documentElement; element.classList.toggle("dark"); const isDark = element.classList.contains("dark"); localStorage.setItem("theme", isDark ? "dark" : "light"); }; document.getElementById("themeToggle")?.addEventListener("click", handleToggleClick); </script>Thus, we’ve created an interactive Astro component using plain JavaScript, without hydrating it as an Astro island. Astro renders the component’s HTML and CSS as usual, while the inline
<script>adds the client-side behavior needed for the light/dark theme toggle. -
Upgrade Dependencies
Upgrade to the latest version of Astro, and upgrade all integrations to their latest versions by running the following commands in your terminal:
npx @astrojs/upgrade