Skip to content

Instantly share code, notes, and snippets.

@jeromeabel
Created April 18, 2023 11:05
Show Gist options
  • Select an option

  • Save jeromeabel/a4482b1e083dd1fb2dcf6a2204579607 to your computer and use it in GitHub Desktop.

Select an option

Save jeromeabel/a4482b1e083dd1fb2dcf6a2204579607 to your computer and use it in GitHub Desktop.

Astro Primer

Ref

INSTALLATION

  • pnpm create astro@latest
  • ✔ Answer: recommended+Typescript/strict+Git
  • ✔ VsCode : extension Astro

STRUCTURE

  • pages : Astro components or React, Vue, ...
  • Layout: common parts nav menu, Header
  • Components
  • Public: static assets

HELLO ASTRO

Clean all files or empty project

pages/index.astro

---
// Front matter

// Add JS
const name = "Astro";
const colorPurple = "purple";
const users = [ 'Astro', "Brad", "John", "Clint" ]
const visible = true; // false
  
// Fetch
const response = await fetch('https://jsonplaceholder.typicode.com/users?_limit=10');
const users2 = await response.json()
---
<!-- JS expression in {} -->
<h1 class={colorPurple}>Hello {name}: { 1 + 2 }</h1>
<!-- Array map -->
<ul>
	{ users.map( user => <li>{user}</li>) }
</ul>
<!-- Array fetched map -->
<ul>
	{ users2.map( user => <li>{user.name}</li>) }
</ul>
<!-- Condition -->
{ visible && <p>I'm visible!</p>}

<!-- Scoped styling -->
<style>
	h1 { color:red }
	.purple { color:blueviolet }
</style>

LAYOUT

layouts/Layout.astro

---
import '../styles/global.css' // CSS
export interface Props {
	title ?: string; // optional
}
const { title = "Astro test"} = Astro.props as Props
---
<!DOCTYPE html>
<html lang="en">
	<head><title>{title}</title></head>
	<body></body>
<html>

pages/index.astro

---
import Layout from '../layouts/Layout.astro'
---
<Layout title="Home Page">
	<h1>Hello</h1>
<Layout>

COMPONENT

---
import logo from '../img/logo.svg'
---
<header class="header">
    <div class="container">
      <div class="logo">
        <a href="index.html">
          <img src={logo} alt="" />
        </a>
      </div>
      <nav>
        <ul>
          <li><a href="/">Home</a></li>
          <li><a href="/about">About</a></li>
          <li><a href="/blog">Blog</a></li>
        </ul>
      </nav>
    </div>
  </header>

<style>
.header {
  height: 70px;
  background: var(--color-primary);
}
.header .container {
  height: 100%;
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 0 20px;
}
.header img {
  height: 35px;
  vertical-align: middle;
}
.header nav ul {
  display: flex;
}
</style>

REACT INTEGRATION

pnpm astro add react

Create a component, import it with .jsx or .tsx

LIST OF CARDS

components/Features.astro

---
import Card from "./Card.astro";
const data = [
    { title: 'Astro', body: 'Lorem ipsum ...'},
    { title: 'Hugo', body: 'Nulla lacus ...'},
    { title: 'React', body: 'Morbi commodo ...'},
] 
---
<section class="features">
    <div class="container">
        { data.map( feature => (
           <Card dark={true} title={feature.title} body={feature.body} /> 
        ))}
    </div>
</section>

<style>
	.features .container {
		display: grid;
      	grid-template-columns: repeat(3, 1fr);
      	grid-gap: 20px;
	}
	@media (max-width: 500px) {
	  .features .container {
		grid-template-columns: 1fr;
	  }
	}
</style>

components/Card.astro

---
export interface Props {
    title: string,
    body: string,
    dark ?: boolean
}
const { title, body, dark = false } = Astro.props as Props
---
<div class={`card ${dark && 'dark'}`}>
    <h3>{title}</h3>
    <p>{body}</p>
</div>

<style>
    .card { }
    .dark { }
</style>

use data in Props

<Tabs headings={['NPM', 'PNPM', 'YARN']} contents={['$ npm create astro@latest', '$ pnpm create astro@latest', '$ yarn create astro']} />

SCRIPT

Downside: the JS script acts on all the page, not the component

---
const activeColor = "red"
---
<p>Hello</p>

<script define:vars={{activeColor}}>
	document.querySelector('p').style.color=activeColor;
</script>

POSTS

Content : posts/test1.md

---
title: Test 1.0
slug: test1
excerpt: Lorem ipsum dolor sit amet ...
date: 2022-05-13
author: John Doe
---

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Curabitur quis porttitor mauris. Sed quis nulla malesuada, imperdiet ipsum eleifend, pharetra lacus. Maecenas vehicula tincidunt lorem sed elementum. Vestibulum luctus ...

List of posts : pages/blog.astro glob + frontmatter object + slug

---
import Layout from '../layouts/Layout.astro';
const posts = await Astro.glob('../posts/*.md');
---
<Layout title="Astro Blog">
    <div class="container">
      {posts.map(post => (
        <article>
          <h3>{post.frontmatter.title}</h3>
          <p>{post.frontmatter.excerpt}</p>  
          <a class="btn" href={`/${post.frontmatter.slug}`}>Read More</a>
        </article>
      ))}
    </div>
</Layout>

Slug: pages/[slug].astro

---
import Layout from '../layouts/Layout.astro';

export async function getStaticPaths() {
  const posts = await Astro.glob('../posts/*.md');

  return posts.map(post => ({
    params: {
      slug: post.frontmatter.slug
    },
    props: {
      post
    }
  }));
}

const { Content, frontmatter } = Astro.props.post;
---

<Layout title={frontmatter.title}>
  <section class="page-content">
    <div class="container">
      <article>
        <a class="btn" href="/blog">Go Back</a>
        <h2>{frontmatter.title}</h2>
        <div>
          Written by <strong>{frontmatter.author} </strong> on {new Date(frontmatter.date).toLocaleDateString()}
        </div>
        <Content />
      </article>
    </div>
  </section>
</Layout>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment