skip to content

What’s New in Astro 2.0?

with Tony Sullivan

The latest major release of Astro includes new features like content collections, hybrid rendering, and a whole lot more. Core maintainer Tony Sullivan joins to show us the latest.

Topics

    Transcript

    Captions provided by White Coat Captioning (https://whitecoatcaptioning.com/). Communication Access Realtime Translation (CART) is provided in order to facilitate communication accessibility and may not be a totally verbatim record of the proceedings.

    JASON: Hello, everyone, and welcome to another episode of Learn With Jason. Today on the show we have Tony Sullivan from the Astro Core team. How you doing?

    TONY: I'm doing good. How about you?

    JASON: I'm doing wonderful. I am super excited to have you on the show. I have been following the Astro project for, I think, almost its entire lifespan. You know, I've been a big fan, and up to the point where I recently took a contract working with y'all. I'm very excited about everything we've been able to do together. And y'all just had a big release. But before we talk about Astro, I want to talk a little bit about you. For folks who aren't familiar with your work, can you give us a little bit of a background on yourself?

    TONY: Yeah, so I'm mostly a frontend developer these days. At Astro, I joined about a year ago. I think I'm coming up on a year in February, actually. I've done a little bit of everything over the years. I've been in software for, I think, 13 years now. So everything from database migration tools to frontend real-time games, some native apps. I was the entirety of a Windows phone team for a mobile app at one point.

    JASON: That's -- yeah, the team of one is always a fun game to play.

    TONY: It is. I had fun booking like the largest conference room in the office for a team meeting a few times just to sit in there by myself and look important.

    JASON: (Laughter) Good. Good, good. We got some folks coming in from the team. I see Jimena. I see Fuzzy. Some friends of the show. George, Adrian, what's up? Hello, everyone. Thank you for hanging out. So Astro 2.0 just dropped this week. That's very exciting. How long has that been in the making?

    TONY: Oh, well, we released the first version, 1.0, last July, I think it was. And we made it about four months before we started hitting things. Like, is this a breaking change? Do we need to think about another major here? Which was pretty good. We got quite a few things fixed and updated before we really started hitting that commitment. We started talking about 2.0 really strongly I think in November, December timeframe. So it was a pretty quick turnaround from us saying I think we need a breaking change to actually getting it out the door and tested and everything.

    JASON: Yeah, I mean, I guess that's always the way it goes. You think it's going to be nice and fast. This is it. This is the last time we're ever going to change this code. (Laughter)

    TONY: This API is perfect. Why would we ever change this?

    JASON: Yeah, the final, final, find actually v1. So as you kind of released v1 and started seeing we're going to need to make this change, it's going to be a breaking change, what were the biggest -- I guess what were the biggest pain points you saw in v1 that drove toward this 2.0 release?

    TONY: Well, one of the main features and I think what we'll end up talking about later is the content collections API we dropped with part of 2.0. That really has -- this is kind of our second iteration of Astro dealing with content. We used to have an Astro .fetch content API. How do you load local files? How do you find markdown easily? How do we fix that developer experience? We pulled that out. I think it was deprecated before the 1.0 release. There were a couple changes to really get that right. There were some changes we wanted to make in the API surface.

    JASON: Right.

    TONY: So that was one of the bigger pushing ones. The hyper rendering one, there were a couple smaller changes. There actually wasn't nearly as much. That was more of a feature addition. I think there was one tiny API tweak that was almost under the covers. But the content collections one was really a big deal. Bumping for Vite 4.0.

    JASON: That's always nice when you get a major in the underlying tooling. And that -- I don't know if it was Vite 4.0 that brought this or something else, but you also have hot module reloading now. Or do you already have that?

    TONY: We've had that, and it's been a long-running project. It's one of those where you can never get it just perfect. There's always bugs that come up. It's really hard to test. So occasionally you break it and don't realize you broke it. There was a period of time, especially leading up to 1.0, where there was a lot of code flying around and HMR was kind of this ever-moving target that we could just not nail down. We feel really, really good about where it is now. Part of that actually was due to some of the under-the-cover changes from Vite.

    JASON: That's great. And I feel like that's one of those things that -- like, obviously we can live without it. I've been able to live without hot module reloading for the vast majority of my career. But once you have it, you're like, I don't ever want to not have this again. It's just so nice to know that I made that change and everything is just running. I'm not worried. It's magical. So what else? I want to spend a little more time talking about hybrid rendering and the content collections, but let's run through a couple of the other high-level things. So we got Vite 4.0. We got a much more rock-solid HMR implementation. What else is happening in Astro 2.0?

    TONY: Oh, geez. There's a long list of really small changes. The other big one that was just such a nice developer experience fix was our new error overlays. It sounds like such a small thing. Oh, great, errors look better. But it makes a huge, huge difference. Erica on our team spent a ton of time really cleaning all of that up and bubbling up errors in nice ways. We went through a full design cycle for that error overlay window. We have Figma designs just for that. Our docs team did a bunch of work partnering with Erica, trying to hook up so an error shows up and you get docs on what the specific error was, how you can fix it, all that kind of stuff. So I think that's up there with what feels like maybe just a DX fix, but it is a huge time saver.

    JASON: You know, this is sort of a pattern that I've noticed with Astro. It's one of the things that I think has been a big source of your strength in terms of kind of growing adoption in the community. You don't skip those details. So these error messages are a big deal. It's in the new in 2.0, but the CLI in Astro is just out of control good. There's a detail that is one of my favorites. We'll look at this later when we actually get to coding, but I'm going to talk through it because I think it's so important. There is part of the CLI where you can add a plug-in. You can add React. You can add Tailwind. You can add whatever you want. When you do that, it doesn't just add it. It's adding the module to your package.json. It's updating your Astro config. It's maybe updating, you know, your TypeScript config or whatever, if it's necessary. And that, in and of itself, is very, very cool. And also, you take the time to show me the changes that are going to be made in my config files and let me confirm those. So as a developer, one of the things that makes me skittish about using generators or things like that is I get this fear that somebody is about to dump a bunch of code into my code base that I will then have to check into Git and maintain. So taking that extra step to show me what am I about to inflict on my repo by running this command just eliminates all my fear. For an Astro plug-in, at least, every one I've run has been a maximum of four lines of code changed, which is great. Cool, I can handle that. You're saving me the effort of having to go in and copy/paste the import, you know, React from Astro.js React. It's little convenience things like that, and that's so pleasant in a world of, well, you just learn that after you've done it a couple times. Okay, but maybe just show me once. (Laughter)

    TONY: Yeah, I got so used to the habit over the years of making sure my Git repository locally was totally clean that I had no changes. So as soon as I run one of those code gen tool, I can at least see what all changed before I commit it. But it's nice not having to do that with Astro. Part of it is a nice benefit of how we design the integrations API. It's really powerful, and you can add two, three, four lines of code in the config file, and that does all you need.

    JASON: At some point in the future, it would be fun to do a follow-up episode where we build one of those integrations. Because it's so seamless, it just now occurred to me that I could build something to modify the behavior of Astro. In my head, I'm like, I'm just turning on React. No, there's code under that thing.

    TONY: The framework ones are interesting. Like adding React or Vue or Svelte. Those integrations get really tricky because they go back to the HMR feature. When you change a Svelte component and Vue recompiles it, Astro helps feed that over to the site. That's not an easy bit of code to hook up.

    JASON: Yeah, yeah. That is excellent. Oh, and I just saw the chat started a hype train. Thank you all for the subs. Jimena, thank you for gifting. Thank you all very, very much. Anarchy, good username. At least I hope I'm not, ah, yes, your name sounds like a word in English. Sorry. If I just made fun of your name, I'm very sorry. Okay. So let's talk more -- okay. Before I do this, is there anything else you want to highlight new feature-wise, before I dive back into hyper rendering and content collections?

    TONY: As soon as we get off the call, I'm going to realize 20 different things I missed and could have gone over, but I think we've hit the highlights. I'm looking back at our blog post. I think we covered the highlights.

    JASON: Great. And we should all be reading this blog post because it is chock full of all the information. Let me find it. Still learning how to type on this keyboard. It's going to be a fun day. Here's the blog. So we're going to drop this link in here so that everybody can see. There you go. All right. So let's talk a little bit about hybrid rendering. This is something that kind of surprised me about Astro because I had always thought of Astro as being the static -- like it was the holy grail for static site generators. It was fast, I could use any framework. It gave me no JS by default. So when I heard that Astro could do SSR and when I heard that Astro could do hybrid rendering now in 2.0, I kind of did a double take. Like, wait, when did they add SSR? So I wanted to talk a little bit about this. That means that Astro is actually capable of more than -- I mean, the vast majority of the internet is static websites. So let's not kid ourselves, everybody. But it sounds like Astro is also capable of doing the dynamic sites as well, if it's got SSR and hybrid rendering. So let's talk a little bit about that. What is hybrid rendering, if I'm being completely honest? I'm not sure I've heard that term.

    TONY: Yeah, we actually had a big discussion internally of how we even wanted to set that up. So Astro, like you said, started out being just static. The whole point from the beginning was we're going to start from a static site generator and then start layering on server rendering and SSR support later. With us basically just taking the gamble that it's going to be easier to go static first and build on top, rather than starting with SSR and then trying to layer static builds on top of that in some way. But once we landed SSR support, which I think was last spring, Matthew on our team, Matthew Phillips, really spearheaded getting that one in. And it was always just kind of a switch. So you either were building a static site, or you were doing fully server-side rendered. We always kind of knew we wanted to have an in between, and we didn't know how we were going to do it. That's kind of where we landed with hybrid rendering. So the big question for us was are you wanting your project to start static and opt into one end point being serverless, or are you wanting to make your whole project SSR and basically opting into a couple of pages being static? So we landed on that one, mainly looking at, you know, kind of how most people are going to really -- that need SSR support, they probably want a static homepage. They want their landing page to be really fast, totally statically rendered. But a majority of their site, maybe they really need SSR support. They're doing a lot of custom, user-specific rendering. Maybe it's an e-commerce site and they want to be checking stock availability for products and that kind of stuff.

    JASON: Sure, yeah.

    TONY: So that's where we landed. Hybrid is basically you flip your side to SSR, but you can per route say this is actually going to be static. And it'll spit out an HTML page just like you're building with Eleventy or any of those tools.

    JASON: When you say this page is going to be static, you don't lose any of the things you can use in Astro. You can still selectively hydrate a React component or put a solid JS mini app in that page. It's just client-side rendered, like it would be in an Astro site the way that so far I have used in the SSG mode.

    TONY: Yeah, so it works exactly the same basically as using our full SSG mode. If there are React components on that page and can be server rendered, they will. Then they'll hydrate in the client like you normally would if you used our client hydration scripts. The only difference -- I've been messing around with this recently, actually. The only difference really is you do export const prerender is true. You're setting one flag in your front matter saying I want to prerender this page. You don't have to worry about client versus server. You don't have to worry about jumping through hoops to do anything different to get that support.

    JASON: That's great because one of my -- I think one of my pet peeves when I'm working with a lot of frameworks is that because they're focused on this idea of hybrid rendering, the challenge becomes that they're building a mental model where everything is the same until you hit the edges, where it's not. Then you're creating a dot server or a dot client file. And this isn't bad, but it's a thing that you just kind of have to know, and it's hard. So minimizing that, where by default, you know, it's always the same file, always kind of doing the same thing that you would expect, and then you have to explicitly opt into different behaviors that add that complexity. That meshes really well with my sort of preferences and world view as a weathered dev. (Laughter)

    TONY: A well-weathered developer.

    JASON: Yeah. Because what I really want is the choices to default to the simplest mental model available. And you know, it's like saying simple not in the context of easy but simple as in the context of fewest moving parts.

    TONY: Right.

    JASON: So I think I like this approach of, you know, you start, it's static by default. You opt into server-side rendering. Then within the server-side rendering, you can opt into individual routes being static. And it's kind of nice to know that you're layering your complexity through an opt-in system instead of trying to opt out of complexity because you don't need it, which I think has been the default in a lot of frameworks these days.

    TONY: Yeah. Yeah, just the ability to quickly look through your code and not have to hold this giant map of where does my end point live, where is my data loading going, what server, what client. It's nice not having to do that most of the time. If your app gets a little more complex, you still could. But you don't have to.

    JASON: Yeah, yeah. I think that is -- yes, exactly. Rule of least power, right. I'm building a homepage for my website. I don't need anything. I just want it to load as fast as possible so that when somebody runs the lighthouse test, nobody can screenshot it and make fun of my on Twitter. That's all I care about for a homepage on a website. Then when I get into the app side of things, sure, then I want more functionality. I want to opt into that so I can say, hey, this app is a dashboard. Everything needs to load fresh on every page load. That's important.

    TONY: Yep.

    JASON: But making that the default and then trying to wrestle my homepage to be fast is just kind of hard.

    TONY: Yeah.

    JASON: So that then, I think, is kind of a clunky segue, but I'm going to use it to talk about content collections. Another thing that I have found as a developer is that I tend to default to markdown from my content because setting up a CMS is tile. I love CMSs. I use Sanity for a bunch of stuff. I like Hasura. I like Contentful, if you're working with big teams. There's a million great CMSs out there in the world. And if I'm working as a solo dev, the effort to set up a whole CMS so I can edit my blog doesn't feel worth it. I just want to write that in markdown because I'm in GitHub anyway, and I know how to write markdown. It's kind of second nature to me because I've written a million readmes. So why wouldn't I blog in that? The trade-off has always been that markdown is just sort of full-blown chaos in your GitHub repo. Your front matter is there. You throw whatever you want in it. You completely forget what you did. Your next blog post has a different set of front matter in it. If I look back through my blogs over time going back to 2014, 2013 when I made my first markdown-powered blog, they are -- it is a tragic tale of no consistency through the front matter in those. Content collections -- is solved too strong a word?

    TONY: Oh, man. That would just start Twitter wars if I told you we solved markdown. I'm not doing that, Jason. (Laughter)

    JASON: I'm going to do it. Astro solved markdown, everyone!

    TONY: Yeah, the interesting thing with markdown versus a CMS, for me, like you said, when it's just you dealing with it, markdown is easier. It's local files. I don't have to worry about setting up a staging environment to test out new content posts. It's local. Until I push it to Git, it's staging, I guess, because it's not live. I was just rebuilding my blog the last few day, actually, moving to this content collections API. I have like 30 blog posts or something, and I found the same thing. Oh, I misspelled that front matter name. That's supposed to say date, and it doesn't. Or why did I call this one tags and the other one collections? I don't know. I just did that day. It silently breaks most of the time. Hopefully I'm handling that tags doesn't exist, doesn't blow up my website when it tries to build statically. But the tags aren't there, and I wanted the tags to be there.

    JASON: Yeah, I think that's been a challenge for me too because I'll have a blog post -- a very recent real use case is when I went solo as Learn With Jason, the business, I wrote a blog post where I wanted my newsletter opt-in to be in the middle because I specifically called it out. I'm going to be doing more. You can get more details on the newsletter. Then a couple paragraphs later, the post ends, and I didn't want to double newsletter opt-in. So I added a front matter field that was like show opt-in and set it to false. None of the other posts have that. So you know, my logic is there. It's fine. But I will never remember that I did that.

    TONY: Right.

    JASON: Like, I'm never going to remember that post has that. And the next time I do it, I'm going to start implementing that feature again before I notice that there's already code. Then I'll go, oh, yeah, I made front matter for that. So the thing that's exciting about this in content collections is -- so I think -- I want to talk about the future goals for these, but the current status of content collections is that you are providing developers with type-safe markdown front matter. So I can define the exact schema. And not just the schema, like what I'm going to use, but the schema, like what I will accept. I can throw an error if I add a markdown field that's not accounted for. I can throw an error if I -- like a really bad problem with tags, for example, is what I would call the ever-expanding bowl of tag soup, where everybody just kind of adds whatever they want, and you don't really look at what's there before. It just breaks and breaks and breaks. So with content collections, I can say only these tags are allowed. That gives me the ability to dot tags and check my auto complete and will show me a list of available tags right there in my IDE. If I want to add a new one, I have to update the schema. And that's fine. I'm a dev. But it prevents me from having one tagged as idea and another tagged as ideas, which is something I do constantly. It's honestly led me to stop using tags because I always duplicate the tags in very similar ways.

    TONY: Yep.

    JASON: And this is like a data management problem, right. But by being able to enforce it through something like type-safe front matter and using actual TypeScript types in that, I get the benefit of I don't need self-control. I have a robot to bat me down and say you can't do that. Then if I'm willing to go and change the robot, that's fine because it's important. But I can't accidently make a mess of my code. Which I feel like is the biggest risk of markdown, really.

    TONY: Yeah, yeah. Even simple things like dates, right. In markdown, a date is really a string. It isn't a date object. There's no validation there unless you do it yourself. It's just a string. And it's very nice starting to see tooling come in that can make sure when you mean for it to be a date, it's actually a date.

    JASON: Yes, absolutely. I think that is a very subtle but huge improvement in my life. If I forgot to put a date in, I want it to throw an error. I don't want it to just accidently drop into the bottom of my list of blog posts. If I forget to categorize it or I use a category that nothing else is categorized as, I want it to tell me, like, hey, did you mean to create a category that doesn't exist? And then I have to say, oh, yeah, I did mean to do that. Or no, right? These are all the things that are just such nice little quality-of-life benefits that you can get from a CMS if you go in and configure your CMS and log into that CMS to edit. But as a developer, as a team of one, it often feels like overkill to do that. Or as developers on a team of only developers, if everybody on the team writes markdown all day, setting up a CMS and asking devs to get out of GitHub and into CMS, no matter how good the CMS is, it's a context switch. But we're all in markdown all day. We're all in GitHub all day. So if your team is a markdown-native team, now we can use these content collections to make things a little less chaotic, even if multiple people are maintaining the markdown blog. So when I saw this for content collections, I got really excited. And I know that Ben Holmes was sort of the driving force behind putting content collections together. So as you're looking at this, how do you see this evolving over time? Is content collections -- are you -- you're at markdown and you're done? Or do you see this expanding?

    TONY: So we've always kind of -- as long as I joined Astro, and even when I was just a community member before that, this team just put such a focus on primitives that we're building into Astro. So we're not solving the world from day one. We're trying to build building blocks that are the basic layer. That's kind of what we have done so far with this first version of content collections. It's just cracking open the door on type validation for markdown. And how you can easily manage content that you're using to build different pages on your site. The next step, there's interesting questions we have around how do we want these content items to reference each other. So you can't really do that right now. That's a tricky problem, right. But you will have a collection of blog posts and a collection of authors, maybe. You want those to reference each other so that you're not copying the author information across a hundred different posts. There's interesting questions that I'm looking forward to us digging into more around images, treating images as content instead of just a single asset file, right. What would it look like if images was its own collection, holding metadata, including the file path but also width and height and format, upload date, and all that kind of stuff. Where we go past that, I'm not really sure. We'll have to kind of see where it goes, how people use content collections. What are people really starting to build on top of it? So we're not getting out ahead of ourselves and trying to solve the world with problems that really don't exist in the first place.

    JASON: And for a dev who is really thinking about how they want to use content collections, where is the best place for them to voice their, like, this is my use case, this is what I want to do?

    TONY: Our Discord is really, really active. If you go to Astro.build/chat, it redirects to our Discord. It's very, very active and very welcoming. People come in all the time with questions or how do I do this or I'd love to have this feature. We just recently, within the last couple weeks, have been revamping our RFC process to help users of Astro bubble up feature ideas and requests and really discuss how they want to be able to use it.

    JASON: And for folks who have never heard of that, what is an RFC?

    TONY: It stands for request for comment, I believe. I always thought it was request for change, but I think it's request for comment. And that really is kind of like the building and pub version of open source. We're asking people what are the features you would like us to build. Usually the RFC process, someone is proposing an issue or like a way -- a feature they need. Hopefully following it up with a potential solution so that we can really kick start that conversation. So it's not just the Astro Core team sitting in a hole deciding what features should we build. We're really getting to pull in user engagement and ideas and opinions. It always makes it better. The more eyes and brains you have working on these things, the better solutions you come up with.

    JASON: So for somebody who's not part of the Astro Core team, which is -- I guess there's a few different Astros, right. There's Astro, the company. There's Astro, the open source project, which has a Core team. And then there's Astro, the community. So for somebody who wants to get involved in the community and maybe work their way up to the Core team, they can just go and look at these RFCs on GitHub and potentially, if they have a proposal, they can propose an idea as an RFC for what they'd like Astro to be. There's not like a barrier to entry on that? You don't have to be this tall to ride or anything? It's like anybody in the community can come in and have an opinion, have an idea. Obviously, you have to get consensus. You have to convince people it's a good enough idea for it to actually happen. But your participation is whatever. You can just go. Today already you're welcome. You can sit with us. Come have an idea.

    TONY: Yeah, for sure. And the RFC process itself is really developer focused more often than not because you're talking about a specific technical problem that you want to solve. Or the solution for it. But there are so many ways people get actually involved in the Astro community. Our docs team has a ton of active community engagement, even for things like translating our entire doc site. Most of those PRs, most of those pull requests come through community members. There was somebody, I think maybe when it got translated to Japanese, that was like, hey, I translated the whole site. Where do I upload it? It's crazy.

    JASON: I love that. And it's great, too, to see a project that gains enough momentum that someone is like, I'm not frustrated not reading this in my first language. I'm going to make this into my first language so I can read the docs more easily. Such a cool thing to see. And when you've gotten a active community that's into that, it's like -- it's magic. So congrats to y'all on building a community like that. But okay. We are about 30 minutes in. I want to make sure we have plenty of time to actually build. So I'm going to switch us over into pair programming mode. I'm going to clear a couple things off the screen first so it's not disconcerting when we get over there. All right. Here we go. So the first thing I want to do is I want to -- I've already shared this link, but this is the Astro 2.0 blog, if you want all of the details on what we're doing today. This episode, like every episode, is being live captioned. We've got a closed caption button on the show itself, once this loads. Come on. Oh, no. I got to start it. The closed captions will start working right now. Okay. This is going to be kind of wild. Yep, there they go, all the way back. They are also available on the homepage right here. You can turn these on with the CC button on Twitch, and they're also available on the homepage. That is being done by Rachel at White Coat Captioning. Thank you so much, Rachel, for being here today. That is made possible through the support of our sponsors. We've got Netlify, Nx, New Relic, Pluralsight all kicking in to make this show more accessible to more people, which I very deeply appreciate. We're talking to Tony today. Tony, you're in the really active on Twitter. Do you want me to even share your Twitter?

    TONY: You can share it. I maybe open it once a month. If somebody wants to tweet at me, I'll see it sometime in February.

    JASON: All right. Here we go. Go follow Tony on the old tweeter. And have a month lag in your conversations with him. (Laughter)

    TONY: We're all async remote these days, right?

    JASON: Yeah. And you're big on Indie Web, too, right? So this is a bit of a tangent before we start. I heard you're cooking up something that might be sort of in the social vein but more Indie Web.

    TONY: Yeah, the content collections opens a new door for what we can do with Indie Web stuff. Basically, I've gone way too far down the rabbit hole of how can I self-host anything I can make on my own webpage. There's a bunch of specs going back to 2005 maybe of open source content types for articles and notes. I've been playing around on my site and trying to see if it would even be useful but to try and make an Indie Web collections helper for Astro using the new content collections API.

    JASON: Oh, very cool.

    TONY: We also do have a new theme that we'll be building sometime soon that our design team just finished up that's going to be a social feed kind of feel. I know Adam Argyle recently rebuilt his site to where it looks like his own Twitter feed. My homepage does something kind of similar. We're going to build a starter theme to let people do that if they want to. Then I'm probably going to sneak a few Indie Web microformats into the theme so that it's all supported that way. I want to say it's nerdy.dev.

    JASON: All right. Let's check it out. Oh, this is cool. So it's like blog posts, or you can go to notes, which looks sort of like micro-blogging, what you would post on a social media network. Things about CSS. Things about Git. Oh, this is brilliant.

    TONY: It's very, very cool.

    JASON: This is what I keep waiting for a social media tool to give me. The fact that I never considered I could build my own, that's a big miss on my part. This is great. I should do this. This is wonderful. So for anybody who wants to see this that is here, we've had Adam on the show before, in fact. We talked about CSS -- what was it called? CSS Open Props.

    TONY: Yeah, open props.

    JASON: And this was a super fun episode, if you are interested in what it would look like if something like Tailwind existed but it was all CSS variables instead of utility classes. It's cool. It's very cool.

    TONY: Yeah.

    JASON: All right. So I've pulled up the content collections API here. And I am ready. I want to try something out. So I think it would be really fun today, let's build out a blog. Let's use the content collections. And I want to look at some of those tricky types that, you know, have tripped me up in the past. Like the errant fields, getting things spelled wrong. But you mentioned dates. I mentioned a list of categories where I start adding nonsense and duplicate stuff. So maybe we can put something together that will help us solve that. If I want to do that, how should I start?

    TONY: I would use Create Astro to start a new project. If you're using npm, I think it's npm init astro. They all have different syntaxes for whatever reason.

    JASON: Here we go. All right. There's Houston. I also just absolutely love this. So let's call this Astro content collections live because I already have an Astro content versions. Which flavor should we be using?

    TONY: I like starting with an empty project because I like building my own CSS styles. But the basics or the personal starter kit have some nice styles built in, if you are looking for that.

    JASON: Great. I'm also going to put this keyboard window over here so everybody can watch me struggle with my ergo. So I'm going to choose -- how about we go with the recommended one? Because why not. That will get a couple base styles. We're not, chat, we're not falling down the rabbit hole of writing CSS for the entire hour.

    TONY: Let's debate BEM for a minute, though.

    JASON: So I need to close my site down. I definitely need to close that down. Boy, I left all my personal stuff up on the wrong window today. All right. Would I like to install npm dependencies? I would. So here we go. Would I like to initialize a new Git repository? I would. I'll use strict TypeScript. It's little attention to detail like this. Again, we were talking about the DX. Does this matter? Probably not, in the grand scheme of things. But does it make me like Astro more? 1,000% yes. Like, it's almost the same as everything else out there, but it's just slightly more charming, and I just dig that a lot. So let's go into the content collections live. I'm going to just open this one up. We will open this in my browser, or in my code thingy. All right. So we've got a favicon in the public folder, nothing else. Then we have a couple basic pieces in here. It looks like we haven't added any plug-ins or anything. We have a base layout. So I'll start this. It's npm run dev, right?

    TONY: Run dev, right.

    JASON: So npm run dev. Copy this. Head over here. And here we go. Here's our basic site. So I want to add a blog, and we can put the blog on the homepage. Maybe we'll just use this card as our blog so that we don't have to style anything. All right. So I think I'm ready then. I want to replace these with links to our blog posts. And to do that, I need to create my first content collection. So what should I do first?

    TONY: I'd pull up the docs. I would never do as good a job explaining this in realtime as our docs team did building out these walkthroughs. So on the left -- oh, going straight to the search.

    JASON: Always to the search. I always forget which top-level heading it is. Then I'm like, oh, no, it's in here someone. Then I'm panicking. Rather than chat having to yell up, down, no, up one!

    TONY: While there's like a half-second delay, just enough to mess with the up/down demands. All right. So these docs will really walk you through getting it totally set up. It starts out kind of explaining what content collections even are. We've kind of talked a little bit about them. There's a lot of really good detail in here on how they work. But if you're curious about getting it started, let's see. There's a section called defining collections. That has a little code snippet in there with an example for a blog collection. That'll be a good start.

    JASON: Oh, perfect. Okay. So I'm going to pull this over to the left. Like so. I'm going to create a new file, and that's in -- it was in content and then config.ts. Okay. Collapse this down. And I think I lost my place when I did this. Defining collections. Here we go. So in here, we're going to import define collection from Astro content. Okay. Now, I don't have this yet, and that's because we haven't -- what do we have to do to get this set up?

    TONY: Is the dev server running right now?

    JASON: The dev server is running right now, and it says types generated. So it could just be that I need to close this and open it again. Whoops. Now it's happy. No, it's not.

    TONY: Defined but never used. Is that all it's complaining about?

    JASON: I'm not sure. It seems -- yeah, you know what, let's try it. So we're going to export const -- oh, boy -- collections. And that is an object. And that has a blog, which is define collection I'm kind of smashing these two pieces together. I don't know if that's a good or bad idea. It makes sense in my brain to, like, have the collection name and what the definition is together. I bet I would change my tune on this if I had multiple collections, though. So then upon doing this -- it's still not quite happy here, and I'm wondering. It says the types are -- blog is not a collection. Check your content config for typos. That's okay because we knew that would happen. And I'm feeling like this is one of those cases where maybe I need to stop and restart the server or something to get it to stop complaining.

    TONY: You could always try that. It should pick it up since the types were built by the dev server. But you never know. Turning the power off and turning the power on again always solves problems, right?

    JASON: I truly -- yep, there it is. So that was it. Just needed to get turned off and on again. Now we're ready. So if I want to define my schema, we can kind of start by thinking through what's actually going to be in one of these, right. If we're thinking about what my front matter would look like -- because I saw from the docs up here, we do a source content and a folder. So I'm going to create a folder called blog. Then inside of that, I'm going to create my first post. We'll just do, you know, first.md. Inside of here, we can start thinking through our markdown. We'll keep it simple here. So there's a few things we want to check. We know we want to have a date. We know we wanted to have -- I wanted to try something with categories. And we want that to be an array. We probably want to have a title for our blog post. Or is it going to pick up this as the title if I --

    TONY: It won't, no. It just parses front matter as data, and the actual content inside the post it deals with a bit differently.

    JASON: Got it. Anything else? What else should we put in here if we're trying to show this off and keep it simple at the same time?

    TONY: We have date. I was going to say some kind of URL. But if we have date, I don't know if that's that useful because it's the same kind of problem, like converting from a string to something type safe. So date, category, title. Those are probably the easiest ones.

    JASON: Okay, we'll start with those. If we have extra time, we can clean that up. So I have these, and I want to make sure I don't make a mess as I go. How would I go about doing that in here?

    TONY: So we're leaning on a tool called Zod to do all our data shape validation, our schema validations. The important thing to know about Zod, really, is it's a runtime check. So if you just define TypeScript types, it doesn't actually make sure when it runs that the types are right. It just knows you told it the shape of the object. Zod actually goes a step further and will throw errors if the data it's given doesn't match the types you said it should be. So in this case, you need -- inside of define collections, you're giving an object, and it needs a key for schema.

    JASON: Okay. So schema.

    TONY: And you'll need to import Z, which is the Zod library. It probably will just find it for you. But it comes in the same Astro content.

    JASON: It found it, but I think because it was one character, it was like, no! (Laughter) And we can see here we're getting all of the different pieces in here. So we've got a whole bunch of different things that it can be. What am I looking for first? What type should the schema be?

    TONY: So the most common thing will be Z.object. Really, for any markdown in front matter, it's going to start out as an object. Inside of this, same kind of thing. It's like turtles all the way down. You're defining objects to build out your schema. So you give it a title, right. You're expecting that to always be a string. So I guess we'll start at the top. I think it should be date.

    JASON: See, this is why we need --

    TONY: We need a type checker for our schema types. Something to yell at us on a second layer.

    JASON: Since you said starting from the top, I am going to put title up top because I think that's going to be the easiest one. So I want this to be a type. I'm not setting a value. I'm setting an expectation of what the value conforms to type-wise.

    TONY: Yep.

    JASON: Okay.

    TONY: And it very much aligns with how you would work with TypeScript. You would just be defining a type or an interface and say title is going to be a string. In the case of Zod, there's helper functions. That makes sure if you gave it a Boolean, it would throw an error. I thought you wanted a string, and you gave me false.

    JASON: So then thinking about it that way, my instinct is we would set this to be date. I have a suspicion that my instinct is wrong.

    TONY: Yes. With markdown, dates are actually strings. So this would be assuming that it's --

    JASON: So if I go in here and do -- what's today? Today is the 26th. This, to me, is a date. But as far as -- this is going to be the string. So then this is a string. But I don't want it to be a string. I want it to complain if it's not a date.

    TONY: Right. So you need to first tell Zod this is a string. They actually have a bunch of chaining. It's almost like the old jQuery libraries where you can chain everything together. So Z.string, and you can tell it this string is actually a date time.

    JASON: Oh, cool. So major type classification, then sub classification. Are there others in here, like other sub classifications?

    TONY: I think URL, email. There's a bunch of crazy ones. CUID is one of their helpers. That's interesting.

    JASON: Yeah, this is wild. So we've got some definite -- oh, you can do a starts with. That's cool. So you can do a lot with this. I imagine if Matt was here, he would say it's possible, but difficult with plain TypeScript types. And also you said this is a runtime check. It's not like once we've built the code, all of our types are compiled away and now we're just trusting that we did that. This is like I can pass in data from an API, and it's going to validate that on the fly. Here's the URL. Okay. Great. So that's powerful. So I have the title is a string. The date is a date time. That's what I want. And my categories I want to be an array. Do I need to set it as an array, or do I want to do a Zod array?

    TONY: You always want to do Zod's helper function. It can also check the types of the objects in there. You're probably just going to be giving it strings. So you can do Z.array.

    JASON: Now, I actually think in our blog, we want to limit the categories. Like, I want to explicitly define my categories. So this feels a little too loose for me.

    TONY: It's very loose. You could throw any string in there, and it would create tags for it.

    TONY: Is there a Z.enum? Their naming convention is very --

    JASON: Yes, aha! All right. And then this is just any array of strings. So I can pass in -- like, we can say -- I don't know. What's the digital gardening syntax? It's like -- oh, crap. I'm going to forget all of this now. We'll just make up a few. We're going to go very, very simple on this. One, two, and three are our categories. I'm going to collapse that side bar so we can see all the code I've written. Okay. So a few things are here that I am curious about. One is can I make it -- like, right now will it complain if I were to come in here and say -- well, actually, let's do a hello world. I'm going to do a category of one. And that feels -- okay, so that's correct. Theoretically speaking, that's all working. I don't think it's throwing any errors. That's good. How do I get it to -- like, will it throw an error if I were to do, like, something like that?

    TONY: I don't think it gets checked in -- I don't think it gets checked directly in the markdown file, which would make for a really interesting addition to the VS code plug-in. I know we talked about that at one point.

    TONY: It would probably be better to do another front matter property for summary or description.

    JASON: Summary sounds perfect. And that's going to be, like, introduction. Perfect. We've done it. That means in here, I need to add a new one. That's going to be summary. And that is just another string. Now, we also have the ability to do things like if the title -- say we want to make sure it's not longer than 100 characters or something. We can do that, right?

    TONY: Yeah, there's a whole bunch of data validation helpers in here. It's probably string.max or max length.

    JASON: And so we can just pass in 100. Now if we try to make a title over 100, it'll yell at us. So now we'll get the URL of the post, the title of the post, the summary of the post. And I want to loop over those to display them. So first thing I need to do is actually load the blogs from the content collection on to this page. And I do that by?

    TONY: So in the front matter, up top, there's a helper function that's actually auto generated from the content collections. You can import. It is get collection, from Astro content.

    JASON: Okay. Auto complete success. And if I'm using my deductive reasoning here, I'm going to make a guess that I would say blogs is going to equal async? Do I need to await it?

    TONY: It is async, I believe. And nicely enough, we have top-level await.

    JASON: That is amazing. Is this being inferred?

    TONY: It absolutely is. That is getting co-generated in the background in realtime. So it knows you have a collection.

    JASON: Look at that. That's dope.

    TONY: It gets even more fun because it also knows the file names that exist in that collection. So it knows what slugs you'll find and what slugs -- it knows you're not going to find some invalid slug that the file doesn't exist.

    JASON: Oh, that's so cool. Okay. So then if I want to use this, I can go blogs.map, and I'm going to put in a blog. We'll do one of these. And then we'll return one of those. Grab this thing, stick it in here. So this is where we should really see the magic come in. What I want to do is not learn anything, and I just want to do blog dot and see what I've got here. So data is where the front matter lives, right?

    TONY: That's right, yep.

    JASON: So I go dot data dot, and there's my stuff. Oh, it's wonderful not to have to know things.

    TONY: I know. And if you marked something as optional, Zod lets you say this field might exist, but it's optional. TypeScript knows. It knows it might be undefined and kind of helps guide you to make sure you handle all those cases.

    JASON: That's amazing. Okay. So this one is a little different because we didn't define a URL for the blog. I'm assuming that was auto generated for us. So if I want my blog post to live at, let's say, whatever the generated slug is of the post, do I have to configure that somewhere, or do I just tell Astro, like, I want it to be/, blog,/, then the slug?

    TONY: So you'll get the slug, which is a file name where the actual markdown file lives. And from there, you could just do a template literal in line.

    JASON: Okay. So it's not including the file path because we did put it in blog. So it doesn't use this as part of generating the route. It just uses the file name itself.

    TONY: Right. Now, you can actually do nested folders. So you could have a source content blog, a folder for EN versus ES, if you're doing multiple languages. The slug would actually include the whole path under there. So you would know this is the English version, this is the Spanish version.

    JASON: Got it. Okay. So this one is the collection. So this is telling Astro this is what my collection is. And anything inside of it will be used in the route. So if we really wanted to, we could do slash blog, slash blog. That feels -- I don't like that. So I'm not going to do that. We'll hard code it to the name of the collection.

    TONY: Yeah.

    JASON: Is this -- is it just going to work when I save this?

    TONY: It will work except the page doesn't exist. So we haven't actually made the layout for displaying the blog post. But that card list should just work. The links will be just be broken.

    JASON: Oh, something is unhappy. Date should be a string, not a date. Oh. Oh, interesting. What did we do? What have we done?

    TONY: Date should be a string, not a date.

    JASON: Should be a string, not a date. So does markdown know enough to turn that into a date?

    TONY: Will I shoot myself in the foot just wrapping it in quotes?

    JASON: Did I kill it? Not a function. That was an invalid thing. Huh. Well, that's cool. Okay. So if you do your dates like this, and this is remark under the hood?

    TONY: Yes. I'm curious, if you put that in quotes, it would blow up and we would see Zod errors.

    JASON: Whoops. Oh, no. Oh, god, what am I doing?

    TONY: I'm making you find the special keys on your new keyboard.

    JASON: Okay. So there. And I broke it. Start it again. Reload. Date should be date, not string. So if you quote it, it is a string. If you don't quote it, then it can figure it out for you. That's great. That's awesome. So, wonderful. You don't have to do the conversion.

    TONY: There you go.

    JASON: So now this is doing the thing. If I click on it, we get a 404. That makes sense. We haven't defined these pages yet. So let's do that. Let's make this work. So I'm going to head back over here, make this a little smaller. And now -- actually, before we get into the page, I want to try that thing again. So we'll do new thing is false. Save it. So it doesn't give me an error. And what happens in I do --

    TONY: It won't give you an error. If you console log, you get from collection. I'm curious if it strips off the unknown field. I think it only keeps the data it knows about. You can change that.

    JASON: I would like to change that. One of the things that always gets me is when you've got a piece of data that is no longer used, but it's very hard to validate that data is no longer used. So you kind of leave it. That's how I found a lot of bigger teams end up with append only files. They're not sure how to do it.

    TONY: Yep.

    JASON: So if I want to refuse unknown front matter, I want this to error. How would I do that?

    TONY: I think -- so we'll go back to the config file where your schema is. If I remember right -- so I know Zod has -- hanging off Z.object, you can do pass through. It's like rest parameters, almost. It copies over the unknown fields.

    JASON: So this will keep it. If I go to index.Astro and do the thing again --

    TONY: So it won't know the type here, but the data would have existed. There's no way for TypeScript to know that field exists because Zod doesn't know about it.

    JASON: Okay, I got you.

    TONY: Now, I think Zod might have a dot strict helper coming off that object. That might be what tells it to throw an error if there's an unknown property. I know there's a helper function for it in there. Somewhere.

    JASON: Not strict. Okay. So that was that error there. Unrecognized keys in object. Perfect. Okay. So that's exactly what I wanted to happen. I wanted it to yell at me if I'm using a piece of front matter I'm not supposed to. So dot strict is the thing. Okay. Then if I set -- what if I don't set this one? It yells that categories is required. What if I don't want categories to be required?

    TONY: They have helpers for that too. It's like they have helpers for everything you could possibly want to validate. It's crazy.

    JASON: As if they have thought through that people would use these things.

    TONY: It's like it wasn't just a weekend hacking project.

    JASON: Okay. So categories. This is what I want to be true. If it comes in, it needs to be an array of one of these three values. But I want it to -- if it's not there, we should just return an empty array, right?

    TONY: Yeah, so you can do a couple things. The easiest here is just after the Z.array call, add optional. You're telling it maybe categories exist, maybe it doesn't. Don't yell at me about it.

    JASON: Okay. So now it's not yelling.

    TONY: So this would basically be building the TypeScript of it's an array or it's undefined. If you're just defaulting to an empty array, you can do that too.

    JASON: Okay. So I'm saving this and it's giving me undefined. So I want it to be an array.

    TONY: Yep.

    JASON: And I would do that by?

    TONY: Take off the dot optional we added and replace it with a dot default function call. And just pass an empty array. And that should do the trick. That's just making sure it's always at least something.

    JASON: Now we're getting an empty array. Perfect. Okay. And it's not mad if the category is left empty on purpose. Great. I thought we might need to mark this one as optional, but it looks like we don't, which is excellent.

    TONY: Yep.

    JASON: Cool, okay. So that's great. That makes me feel good about that because it lets us do things like skip the front matter we don't need without throwing an error, if it is, in fact, optional. Like a category. Then we can get real strict on stuff. We can make sure no unused fields are passed through. No categories we haven't defined get passed through. That's all great. Now I think we're ready. Let's build that blog page. I'm going to make a guess it's in this pages directory.

    TONY: You got it. Astro has always had file-based routing. You would end up with a folder called blog. Inside of it would be in square brackets slug.astro.

    JASON: Like that?

    TONY: Yep.

    JASON: Okay. So I've got my slug.astro. And then I'm going to make an assumption that we're going to do something in the front matter. So I'm going to make that little block.

    TONY: Yep.

    JASON: Then what is the -- I'm going to get the layout. We'll just stick that up here. And what needed to go on the layout? I think a title.

    TONY: I think it was just a title, yep.

    JASON: So I want the title to be my blog title. So I'm going to wait on that. We need that. So we'll do the rest of the layout after that, but I need this to be -- oh, you need to go up a layer. So we need to get one blog post based on whatever the slug is. So in this particular case, when I do slash blog, slash first, the slug here is going to match that first. Then we can use that to look up the blog post, correct?

    TONY: Right.

    JASON: Okay.

    TONY: Yep, and we're doing this as a static build. This step gets a little different if you're using SSG versus SSR. We'll do it the static build here. It's your blog. You don't need server-side render for that.

    JASON: Exactly.

    TONY: Okay. So go ahead and import, just so we have the helper function around. You're going to import again from Astro content. I want to say it's called get entry, by slug.

    JASON: We can check. Nope, not like that we can't. Like this.

    TONY: For static builds, the key here is the route needs to know all of the different blog pages you're going to be spitting out as part of the static build. So Astro looks for you to export a function called get static paths. You'll just export it straight out of this front matter. You'll probably want to mark it as async.

    JASON: Okay. Async function, get static paths?

    TONY: Yes.

    JASON: Okay. Do I need any --

    TONY: I don't think you'll use them here. In fact, let me pull up our docs just to double check because we have a bunch of stuff on that one too.

    JASON: So if we're going to tell it to get the static paths, wouldn't we need all of the posts?

    TONY: Right. So this is where the performance question gets really interesting.

    JASON: Okay.

    TONY: So you want to know all of the different blog posts that need to be built, but this doesn't matter as much with the static build. If your build is a little slower, who cares? But with SSR, if you had to scrub and render a hundred markdown files to find the one you needed, you have a real performance problem there because your server is going to be a lot slower. So get entry by slug, which we're going to use here, will find all the blog posts and give you back their front matter. It doesn't render the HTML right away. It just gives you back the front matter and kind of a hook to render only the blog post you want.

    JASON: Oh, gotcha. Okay. That's excellent. All right.

    TONY: So let's do something like const all blog posts, I guess. And we'll actually also need get collection in here.

    JASON: Okay. And I'm going to await that, right?

    TONY: Yeah.

    JASON: Love that auto completes. Okay.

    TONY: And all you'll really need to do here, get static paths returns an object. Sorry, an array. And it's an array of objects with the parameter. So in this case, just an array of objects saying what the different slugs are. So you'll return probably return to all posts. You want to walk through each post that's there, and we're basically going to spit back out the slug that comes from each entry.

    JASON: Okay. So we are going to return the object. And that has -- is it params?

    TONY: It's called params, and that's also an object.

    JASON: That's also an object.

    TONY: Yep. So we named the file slug.astro, which is why we're using slug here. It actually could be whatever you wanted to call it there.

    JASON: As long as this one and this one match, right?

    TONY: Exactly. Exactly. We use the params object to figure out which file to render.

    JASON: Gotcha, okay. So we're doing that. And then do we pass anything else?

    TONY: You can pass back the props here. And if we do, we just actually wouldn't need to call slug at all. So along with params, this object could have a props object on it.

    JASON: So you do it like this. Then we would put the post in like that?

    TONY: Yeah, I'd probably just do it that way and go for the shorthand. So at this point, Astro knows all the different pages you're going to build.

    JASON: Got it. Do we -- did you want to do it a different way to show off the get entry by slug?

    TONY: Let's do it this way first. Then we'll just kind of show how get entry by slug would work. That'll be what people will usually reach for, for SSR.

    JASON: Okay. So when I've done this, it might complain. It's not complaining. I'm going to click on this. And we get something empty because I haven't put anything on the screen yet. But if I were to put something on the screen, like for example -- and close that tag again. We get it back. Now we need to actually get the pieces in, and I need to access the post, right?

    TONY: Yep. So after that get static path function, just drop down. And it already exists at this point on the Astro global when it's trying to render out this page for you. So you can do const and destructure the object so you're looking for post.

    JASON: And it's on the global --

    TONY: Astro.props.

    JASON: So I can get the post now. There it is. So then I can do what I wanted up here, which was post.data. Where's my auto complete? I'm missing my auto complete. I'm panicking. (Laughter)

    TONY: So astro.props, it's really tricky to try and feed that type through. I'm hoping at some point we can find a good solution. It just knows that's any. So I usually typecast the astro.props call. So at end, I'll give it an object type.

    JASON: Okay.

    TONY: It has a post.

    JASON: Oh, it has a post. Post.

    TONY: And there's going to be a type you can pull out of the Astro content layer. I believe it's collection entry.

    JASON: Collection entry. Yep, there it is.

    TONY: And you give it the little angle brackets.

    JASON: Pointy boys.

    TONY: Yep, the little pointy boys.

    JASON: Oh, and it auto completes yet again.

    TONY: So hopefully one day we'll be able to hook that up for you.

    JASON: There's auto complete now. Now we have everything. I'm going to drop in my title. We'll do -- I'm just going to save it and see what happens. It updated the front matter title but not the rest. So we can do post.data.title. We can jump down a little bit and do a category. I'm realizing now I probably wanted my categories to be not an array, but that's all right. We're going to make it work anyway. We'll do a post.data.categories.join, and we'll join them with one of those. All right. Then I'm going to skip the summary. Instead, what I want is to show the content of the post. As far as I know, we don't have. Will post body just work? I'm guessing that won't.

    TONY: Post body is basically the raw markdown, yeah. But if you open back up that auto complete, post.render is a function.

    JASON: Post.render. And that is --

    TONY: I'll usually do this in front matter.

    JASON: Okay. So we want this in the front matter because I'm going to need to await it.

    TONY: Yeah, inline JSX function calls to me is just too hard to follow.

    JASON: Okay. So I don't know what that's going to return just yet. So I'll do one of these. And then I'm going to set equals await and drop in this post.render. And let's see what's in here. So I'm going to hit control space. And we get the remark plug-in front matter, the headings. Headings is really useful if you want to build a table of contents or something. That's it. There's your table of contents. And then we've got the content, which I'm assuming is my actual content. I can see here that it's an Astro component.

    TONY: Yep. Drop it in just like any other Astro component where you're wanting to render the body. That's all you got to do.

    JASON: Sick. Okay. That's extremely helpful. Okay. So then that kind of changes the way we want to do this because we got our script right in there. If I add in one of my categories, I'll do one of these. Do one of these and break it. Not allowed. You got to do one of the allowed categories. Okay. So I've been rescued from myself. We're showing our categories. We're showing all these pieces. And I can do something to the effect of -- we'll just add like a -- is there a preferred way to do local lengths? Like if I'm just doing an href is that preferred?

    TONY: There is no link component.

    JASON: Oh, god. That's not the one. Everybody look at my keyboard. What is the ampersand? Oh, god.

    TONY: Might be on a layer.

    JASON: Yep, on a layer. Here we go. We're going to this one. There's no ampersand on this layer either? Oh, no. Hold on. Oh, wait. I can cheat by looking at my other keyboard. Who knew this would be my undoing? (Laughter)

    TONY: The ampersand ruined the Ergodox for you.

    JASON: There we go. Now we have a mostly navigable, nice setup. We can get around in posts. Then if I wanted to make another one of these, I can come out here and copy/paste. We'll change this one to second. And another one. This will be category three. That's when you know you're really blogging, when you write that second post. All right. So now we've got our content collections here. We're rolling type safe. We've got -- you know, I can't add junk in here. If I try to add junk, I get warnings. I'm going to add a little bit more junk here for the sake of showing off these error overlays because we didn't actually give them a fair look. I think I saw Erica in the chat a minute ago. Let's break it. So we'll do bad, no. And now it's going to yell. Let's just take a moment to look at these gorgeous error overlays.

    TONY: Yeah, pull that window open. Let's look at that. We even have Houston hiding up in the corner.

    JASON: Yeah, this is great. So we've got a dark mode and a light mode switch, which is awesome. It's running on my dev server, which I don't know -- I think I confused it. Yeah, now it's dark mode. I won't toggle it back to light mode. But we have a useful heading as the error message. We've got a really easy-to-parse piece of context, a link to the docs, which is just chef's kiss. It actually shows you your code, which is wonderful. And then it gives you the full dump of the error code as well, which is also -- you know, it's useful. If you like reading error stack traces, you want to be able to have access to it. This is pretty excellent.

    TONY: Yeah, it's very cool the way this bubbles up. And it's always tricky, Astro supporting every framework that's out there, but you'll see React errors, Svelte errors, Vue errors bubble in a nice way to give you as much context as it can.

    JASON: Yeah, this is slick. Again, it's little things like this, I think that, really help when you're trying to adopt a new framework, when you're trying to get your feet wet with something new. When you can tell the framework authors have taken the time to consider not just what happens when you know it all but what happens when you don't. What happens when you're going to be stumbling all over the place, going to be doing things wrong? You're going to put something in the wrong place. How does that -- what is that experience? Like what's my experience of making a mess, and how much do you help me as a framework clean up my own mess? And this is, I think, just a really nice touch that makes me feel like I'm going to be okay. I can't make that big a mess in Astro. So jaguar is asking, you can choose the libraries, you can use React or Svelte? Yeah, because we can use just about anything at this point. React, Svelte, Solid, Vue. What else?

    TONY: Preact, Lit, like the custom element framework.

    JASON: Yeah. Pretty slick.

    TONY: There's a community-driven Angular integration that gets you Angular support.

    JASON: Mm. Mm-hmm. And that's incredible. How is that possible is an answer that's going to take a little bit more than we have time for. But I do think I'm going to be harassing y'all about doing a follow-up episode where we build a plug-in for Astro. I think that would be really, really fun.

    TONY: Yeah.

    JASON: The team is trying to get you to say X element, and I don't know why.

    TONY: Is that Peter? Is that Fuzzy?

    JASON: It is.

    TONY: So X element is actually very cool. It almost inlines custom element kind of features through an Astro component.

    JASON: Oh, interesting.

    TONY: It feels like you're using custom elements. It feels kind of like you're using a different framework. But it's inlining scripts directly.

    JASON: Oh, nice. That's super cool. All right. So I think at that, I'm going to call this one a resounding success. We were able to build out a fully type safe markdown front matter blog. We wrote out our Zod definitions. We fed those into our Astro site so that we were able to kind of write these. Then get our auto complete throughout the post. We generated a single blog page post and a roll. This is a really nice API, and I like it because it doesn't prevent me from doing what I want to do, which is write markdown and it lets me add some safety to the way I write markdown so I can't make quite as big a mess by having the no rules that typically apply to a markdown file. So Tony, is there anywhere else people should go if they want to dig deeper into all of this?

    TONY: Yeah, definitely check out our docs. The docs team, both people in the Astro Core team but also a ton of community members, spend so much time building what I would say is like the best docs experience out there right now. It is crazy the amount of detail that we go into here and the different language support. So the content collections, Ben also spent a ton of time as part of getting this feature out actually documenting it, which is harder than you think when you're just like, oh, I'm going to code this feature and kick it out the door.

    JASON: Yes, yes. There's the downfall of so many projects. I'll document it once I'm done. It's never done, so it never gets documented.

    TONY: Well, it's the problem with domain-driven development. You buy a sweet URL, come up with an idea on a weekend, and you never document it and it never goes anywhere.

    JASON: What did I say in the pregame about no personal attacks on the show, Tony?

    TONY: That is how our whole team operates. You're in good company. (Laughter) Nate and Chris on our team are experts at finding crazy domain names that should not be able for 30 bucks.

    JASON: Oh, I love it. All right. So I'm going to send everyone your way one more time. Give Tony a follow on the old tweeter. And remember it's a month lag. And make sure you go over to the Discord because -- here it is. The Discord is where all the magic happens. There's a really active community over there. Great place to go if you're learning, if you're building, if you want to show things off, get some feedback. Hang out in the Astro Discord. It's pretty wonderful. Tony, anything else you want to include before I take us home?

    TONY: I don't think so. I hear we have some interesting hybrid routing streaming coming up here soon.

    JASON: Yes. We are going to go raid Ben Holmes on his channel because they're going to keep the Astro train going with some hybrid rendering stuff. This episode, like every episode, has been live captioned. We've had Rachel here from White Coat Captioning. Thank you so much, Rachel, for being here. That's made possible through the support of our sponsors. Netlify, Nx, New Relic, Pluralsight all kicking in to make this show more accessible. Thank you so much for spending me money so that I can spend it on making the show better. Tony, thank you for spending some time with us today. This was an absolute blast. Chat, as always, thank you all so much for being good sports and playing along and watching me struggle on this new dang keyboard. I think that's it. I think we're done. Tony, thank you very much. Y'all, we'll see you next time.