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
mdomdx - 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
.mdo.mdxel layout - Las props se pueden utilizar como variables dentro de llaves, ej.
{frontmatter.name} - Si se utiliza
collectionsy 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>