Saltar al contenido principal

Blog y Colecciones

Colecciones

  • Más fácil de mantener
  • Tipado estricto para las entradas de blog o contenido
  • Facilita las secciones como autores, tags, etc.
  • Permite tener una estructura o tipado estricto para los archivos md o mdx
  • Anteriormente se hacía con File System, posts/*.md

MDX

  • Markdown no necesita integraciones
  • Si se utilizan archivos MDX entonces es necesario instalar la integración
bun astro add mdx

Ejemplo Post

  • Las variables definidas en el frontmatter se envían al layout como props.
  • Se pueden tipar las props
  • Se debería agregar en cada archivo .md o .mdx el layout
  • Las props se pueden utilizar como variables dentro de llaves, ej. {frontmatter.name}
  • Si se utiliza collections y se hacen referencias, por ejemplo el nombre del autor, este debe tener el mismo nombre que el archivo, ej. jhon-doe.yml
---
title: Flutter!!!!
date: 2023-06-01
description: Aprendiendo FLutter.
//author: Jane Doe
author: jane-doe
// image: https://placehold.co/1400x900/
image: "/assets/images/post-01.png"
tags: [Flutter, Dart, Programming]

layout: ../../layouts/BlogLayout.astro
---

# {frontmatter.title}

{/* <!-- Mostrar imagen --> */}

<img
  src="/assets/images/post-01.png"
  width="350"
  alt="Explorando Funciones de ES6"
/>

Colecciones

  • Se debe usar la carpeta reservada src/content/
  • No es necesario definir el layout en cada archivo markdown, sino que se hace en la configuración general

Config

  • Hay dos tipos de colecciones: content; como markdown y data; metadata
  • El nombre de la colección al exportar debe tener el mismo nombre que la carpeta donde estarán guardada, src/content/blog, src/content/newsletter
import { defineCollection, z } from "astro:content";

const blogCollection = defineCollection({
  type: "content",
  schema: ({ image }) =>
    z.object({
      title: z.string(),
      date: z.date(),
      description: z.string(),
      image: image().refine((img) => img.width < 1200, {
        message: "Image width must be less than 1200px",
      }),

    // Relation with another collection
    author: z.string(),

    // Relation with another collection
    tags: z.array(z.string()),
  }),
});

// Export all collections
export const collections = {
  blog: blogCollection,
};

Pagina con layout y slug

src\pages\post[slug].astro
---
import type { GetStaticPaths } from "astro";
import { getCollection } from "astro:content";
import BlogLayout from "../../layouts/BlogLayout.astro";

export const getStaticPaths = (async () => {
  const blogPost = await getCollection("blog");

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

  // return [
  //   {
  //     params: { slug: "post-01" },
  //     props: { title: "Post 01" },
  //   },
  // ];
}) satisfies GetStaticPaths;

const { post } = Astro.props;
const frontmatter = post.data;

const { Content, headings } = await post.render();
---

<BlogLayout title={frontmatter.title}>
  <h1 class="">{frontmatter.title}</h1>
  <h4 class="">{frontmatter.author}</h4>

  <h3>Tabla de contenido</h3>
  <ol>
    {
      headings.map((heading, index) => (
        <li>
          <a href={`#${heading.slug}`}>
            {index + 1} - {heading.text}
          </a>
        </li>
      ))
    }
  </ol>
 
  <Content />

  <div class="my-20">
    <a href="#btn-back">Ir arriba</a>
  </div>
</BlogLayout>

Index (home)

src\pages\index.astro
---
import { getCollection } from "astro:content";
import TypedBlogCard from "src/componentes/TypedBlogCard.astro";
import { siteConfig } from "../config/site-config";
import MainLayout from "../layouts/MainLayout.astro";

const blogPost = await getCollection("blog");
---

<MainLayout title="Blog">
<h1 class=""> {siteConfig.title} </h1>
{blogPost.map((post) => <TypedBlogCard post={post} />)}
</MainLayout>

Componente con props

---
import { Formatter } from "@utils/formatter";
import type { CollectionEntry } from "astro:content";

interface Props {
  post: CollectionEntry<"blog">;
}

const { post } = Astro.props;
const frontmatter = post.data;
---

<div class="">
  <!-- <img
    class="object-cover w-full h-56 rounded-lg lg:w-64"
    src={frontmatter.image}
    alt={frontmatter.title}
  /> -->
  
  <Image
  class="object-cover w-full h-56 rounded-lg lg:w-64"
  src={frontmatter.image}
  alt={frontmatter.title}
  width={500}
  height={500}
  quality="low"
  loading="lazy"
  />
 
  <a
    href={`/post/${post.slug}`}
    class=""
  >
    {frontmatter.description}
  </a>

  <span class="">
  {Formatter.formatDate(frontmatter.date)}
  </span>
</div>

Sin Colecciones

Estructura

src  
┣ layouts
┃ ┣ BlogLayout.astro
┃ ┗ MainLayout.astro
┣ pages
┃ ┣ posts
┃ ┃ ┣ post-01.mdx
┃ ┃ ┣ post-02.md
┃ ┗ index.astro

Layout

  • Extraer los props del frontmatter
src\layouts\BlogLayout.astro
---
import { ViewTransitions } from "astro:transitions";

import "../styles/blog.css";

// interface Props {
//   title?: string;
// }

const { title, ...rest } = Astro.props;
const frontmatter = rest.frontmatter;
---

<html lang="es">

  <head>
    <title>{frontmatter.title}</title>
    <ViewTransitions />
  </head>
 
  <body>
    <main>
      <slot />
    </main>
  </body>

</html>

Index (home)

  • Glob permite buscar una carpeta en el proyecto (en tiempo de construcción) y leer todo el filesystem (markdown y su frontmatter)
src\pages\index.astro
---
import BlogCard from "../componentes/BlogCard.astro";
import { siteConfig } from "../config/site-config";
import MainLayout from "../layouts/MainLayout.astro";

// Glob
const posts = await Astro.glob("./posts/*.{md,mdx}");
---

<MainLayout title="Blog">
  <section class="bg-white dark:bg-gray-900">
 
  <h1>{siteConfig.title}</h1>
  {posts.map((post) => <BlogCard {...post} />)}
 
  </section>
</MainLayout>

Componente

---
import { Formatter } from "@utils/formatter";

const { frontmatter, url } = Astro.props;
---

<div class="lg:flex">
  <img
    class=""
    src={frontmatter.image}
    alt={frontmatter.title}
  />

  <a href={url} class="" >
    {frontmatter.description}
  </a>
   
  <span class="">
  {Formatter.formatDate(frontmatter.date)}
  </span>
</div>

Imágenes

  • Las imágenes no deberían estar en la carpeta public para que Astro pueda optimizarlas
  • Se puede crear una carpeta junto a los posts
  • Se utiliza el componente <Image>