Crash Course Into Astro, and Portable Text


During this crash course, we’ll create a new project in the headless CMS, create the content model and data using the CLI. Then, we’ll use the content to build an Astro website including front-end components and rich text resolution using Portable Text.

This will be hands-on workshop, you’ll need VS Code, Git, NPM and basic knowledge of JavaScript. Don’t worry, I will explain all the steps as we advance through the workshop and you will be able to directly ask any questions.

91 min
22 May, 2023


Sign in or register to post your comment.

AI Generated Video Summary

Today's workshop covers the basics of various topics, with a focus on Rich Text Resolution in Headless CMS. Participants will learn how to import content, create pages and components, generate TypeScript models, and resolve rich text. The workshop also discusses portable text and its benefits. The use of Astro as a front-end framework is highlighted, along with its compatibility with React, Vue.js, and vanilla JavaScript. Participants will also learn about working with environments, fetching data, and adding custom components in Astro.

1. Introduction and Agenda

Short description:

Hello everyone, I'm Andrej. Today, we'll cover the basics of various topics and focus on Rich Text Resolution in Headless CMS. We'll import content, run a blank Astro project, create pages and components, generate TypeScript models, implement a listing page, and resolve rich text on the homepage. We'll also discuss portable text. You can find all the necessary links in the repository. You have the option to register for or use my project ID. Let's clone the repository and get started!

So hello, everyone. I'm Andrej. Thank you for being here. This should be a fun evening. I'm saying hello from the Czech Republic. These are all quite wide topics. So I'll try to cover at least the basics. I don't want to tell you everything about each of these topics but just give you an overview of how it all works together. Then of course if you're at the conference we can talk about it more there or you can find resources for all these online as well. My aim is to just have a good time for the next hour or two and we'll see hopefully you'll take something from it.

So I'm Andrej, I'm a developer evangelist here at We're a headless CMS vendor, so we are quite a bit in the field of websites, frontend development, Javascript and so on. My personal passion used to be.NET because I started as a.NET developer. These days I do pretty much everything, whatever is expected. So Astro is one of these projects where this was just the Nuke hit on the block and it was fun to do. That's me. I'll share the content details right after the workshop.

But let's take a look at what we're going to cover today. I want to make it brief. Nobody wants to be here for four hours. This is what we're gonna do. This is the website that we will finish at the end of the workshop. It's just a sample site for an airline. We call it Contenare because we are So it's quite a nice pun here. What we're going to focus on is the Rich Text Resolution because that's the biggest task of any website that is using Headless CMS. Now the agenda for today is first, this is gonna be a hands-on workshop. If you have your Visual Studio Code installed and ready to use, if you have Git, that's perfect. I'll just always give you a few minutes to do what I'm doing. But there's not gonna be a 15 minutes block where you will be working on your own. We don't have that much time for that. So what we're gonna do is import content into the CMS project. Then we're gonna run a blank Astro project. Now I've already created one for you so I'm just gonna give you the Git repository so you can clone that. So we don't have to waste time on that. We'll create two pages and a few components in Astro, so that you can see how easy it is to create these components. We'll generate strongly typed TypeScript models. It's… I call them models because as I said, I used to be a.NET developer, but these are actually types in TypeScript. But there's a script for those so that should be also quick. We're gonna implement a listing page that will list all the data that we have in the headless CMS. We'll implement rich text resolution on the homepage. And that's the end of it. We're then gonna turn the rich text resolution to portable text. I'm also gonna tell you what portable text is, if you're interested in that. So that's the agenda for today. That was I said, for every part of that I'll give you time to work on that. Let me see. Okay, here we go. So just to share quite a few important links. This is the repository that we will be working with. It's actually under my GitHub. I'm going to copy that link in the chat in a second so you don't have to write it from the slide. There is a links.txt file within that repository that contains all the other links. No need to write anything on your own, you can just do a copy-paste. Now as I mentioned, we're going to be using as the headless CMS as a content storage. Now, there are two options for you. Either you can register for a free plan or a free trial and do everything on your own. So I'm going to give you the import scripts and everything, it's quite easy to set up. Or I'm going to give you a project ID of my project and you can use that. Depends on what you're after. It's easier if you register on your own and import your own data and you can play around with it. You can click around the UI. But both options are possible. If you don't want to register. So now will be the time to clone the repository. So let me just copy the URL in the chat window. If I figure out how to open Firefox when zoom is here. Let me see. There's the linux.txt file. And there is the registration.

2. Register, Import Data, and Set Up Environment

Short description:

To get started, register and log in to the CMS. Then, import data into your project using the content.aicli. Make sure you have the necessary API keys and project ID. Clone the CLI repository and set up the environment. Refer to the Workshopx.pdf for detailed instructions.

If you want to register on your own, it's gonna take just a couple of seconds. So you can just copy that link is right here. And paste there. Obviously I'm already logged in. So I would just use sign in. But for joining, just put your email down and pick a password and get in there. I'm gonna give you a few seconds to do that while I fight my Firefox and other extensions.

Okay, now when you're registered, you should be seeing this but completely blank. So let's see if we can get through that. And of course, when you're starting with a blank project, the first step would be to actually import some data into the project so that we have something to work with. There are a couple of ways we can do that. The first one is a backup manager. So if you have your project, you then back it up into a zip package. You can use the backup manager to then import the zip package into another instance or another project. Today, we're going to use a better solution, and that is the content.aicli. Now, there is the next link below the registration is for the repository that contains all the data. So once you're done with the registration, this will be the next step to get to the CLI project. I'm going to clone that as well, if you can. I have both of these already cloned on my local so I'll just give you a few seconds to do the same. So just take the URL and clone it into your local.

All right, I'm going to open the project in Visual Studio Code. Now, just let me know in the chat when you're seeing the same thing as I am, so I know we can continue. Actually, this is the main project. This is the one I'm working on. There's another one. This is the CLI. Now, in the meantime, I'm going to create a new project for me too so that I can run the import with you. I can actually delete all of these and we have a blank project just like that. So just close this and delete all of these and remove the content apps as well. This is a pretty much empty project, so I can work with that. Let me know in the chat if you need more time. If you already have the project set up and the second repository cloned so that we can move on. Just to be sure, I'm going to put the second URL in the chat as well. But I hope that you are all finished by now. If not, let me know in the chat, yeah? I can wait or I can explain something further. Let's see that we are all here in the Content-Aware CLI. That's the second repository that I showed you. If you cloned that, that's perfect. Now, in order to import the data into the system, we're going to need two things. We'll need the delivery API ID. Every project that you create in the CMS has a project ID. There's this URL here. That's this part of the URL. It's also in the settings under Environment, ID. So if you go to Environment, Settings, General, then the project ID is right here. So I'm just going to copy that. I put it somewhere. Could be here or anywhere else, doesn't matter. I'm just going to create a new text file. Just put it here. This is the project ID. And there's another thing that we'll need. If you go back into the CMS, there is an API key section in the Environment, Settings, and we're also going to need the Management API. Now the Management API is an API that lets you edit content in the CMS. Now, the use cases we need to import data into the CMS. So we definitely need a management API to do that. So if you don't have that enabled, if it's a blank project, you first need to enable it with the toggle here and then generate your API key. First, it's not going to be here, you have to define the validity of the API key and then you'll be able to copy that. So copy that into the text file as well. So two API keys, right? One is for delivery API, one is for management API. And what we're going to do is we're going to create a new environment for the CLI. So let me show you the GitHub repository of the CLI where we have the examples. So let's go into repositories and look for CLI. It's this one. So for the setup, you're going to need to add environment, right? That's this part right here. It's also, by the way in the presentation that I've shared. So if you look at the main project, the first repository that I showed you there is a file called Workshopx.pdf. So in here, you're going to see the whole presentation that I'm showing, which also contains all the important commands, right? So feel free to use that as well. But right now, I'm just going to copy the command from the repository. So that's the content environment add-in.

3. Setting up Environment and Importing Data

Short description:

To set up the environment, name it in Visual Studio, install NPM dependencies, add the project ID and API key. Confirm the setup and import data into the content project using the 'NPM run migrate all' script. This initializes the project by creating content types and data. Refresh the CMS to see the imported content and publish the items for them to be included in the delivery API response.

Then we need to name the environment somehow, and set up the project ID and API key. So I'm just going to do that in the Visual Studio. So this is the environment. This is the first project we first are going to need to install NPM. So npm i, to install all the dependencies including the content CLI. And then it's a content, let me see, content environment add. Content environment add, name is going to be depth. And then we're going to set up project ID. And project ID is the first one. And API key, which is the second one. Cool. Now that we have both of them there. Okay. Content environment, add name, project ID and API key. Now that we have both of them there, you can confirm that and it's going to create a new file that has, uh, what did I do wrong? I'm no arguments. Content environment, uh, should be two dashes here. So, two dashes for name now, we should be good. Okay. Now, in my case, the deaf environment already exists. Uh, we'll be overwriting in your case, the file should not be there. So, uh, once you run this, you should see the environments that Jason file, and it should have the project ID and API key. So I'll give you a minute to, to finish this. Let me know if you have any issues. Um, but this actually means we can start importing data into the content project. Okay. Once you're ready with this, um, there's a script that we can run. Which is called NPM run migrate all this means that we're going to take all the data that are in the migrations. So you see that there are some files in the migrations folder. These files actually just define the content types that we're going to be creating in the CMS, right? There's probably no need to go into more detail on this. Um, as, uh, these are tested and I know that they work, but it's going to initialize the project for us. So I'm going to run NPM run migrate all, and this is going to take a second, um, because it creates a bunch of content types and bunch of data. Now let me just remove that and run the migration again, because there is a status dot JSON file that, uh, you know, tracks the progress of your migrations. And in my case, um, it thought that, uh, all the migrations have already. Um, then done. So we see now it's actually important data into the system. So if you're running the same thing, uh, you should be seeing similar results as I am. Now the last migration takes a bit long time, um, because it also uploads a one, a bit larger video. But it should be executed. So now it's executed successfully. Now, when we go back into the CMS, we should see some content here. Number two, the refresh the window. Because I just touched, you know, the items via API. We should be seeing 54 content items here in the repository. Uh, while we're at it by clicking this button at the top, we can select all of the items and just publish them. So before, um, when, when the items were in draft, they would not be a part of the delivery API. Uh, the CMS will only, uh, include the items that are in that are already published in the data API so that you can get them via the API. Uh, everything that's in draft or other workflow steps will not be in the delivery API response. So that's why I'm actually publishing it now, because I just want to consume the data. I don't want to process them any further. Right. Um, okay. I'm going to give you one more minute to finish this and publish all the items. Uh, then we're going to go back into the presentation or let me know if you need more time, uh, or you don't need to explain something.

4. Introduction to Astro and Running the Empty Site

Short description:

Now that we have the content in the CMS, we can move on with the workshop. Astro is a front-end framework that focuses on making client bundles as small as possible. It works with React, Vue.js, or vanilla JavaScript. Astro is a good fit for smaller sites. The next step is to run the empty Astro site and install the necessary dependencies. After running 'npm run dev', you should see the homepage at localhost:3000. The plain Astro site has a file-based routing structure.

Okay. Now let me know if you need more time. Uh, otherwise I'm just gonna move on from this. So now that we have the content in the CMS, uh, we can move on with the, with the actual workshop, right? This was just a prerequisite for, for everything. So, uh, first of all, uh, well, I also explained what the content of AI is, to our headless CMS. Uh, now we just imported all the data. So it's quite clear what the CMS can do for you with the APIs. So, uh, I think we can, we can skip that as well. If you want to know more about content, I'll share the link for, uh, for, you know, getting in touch or our website later.

Now, what is Astro? Astro is a front-end framework, or a way of building websites that is particularly popular these days, because, uh, it, it goes exactly against the trend of the last years. Because last year it was React. Right. It was Next.js, it was Gatsby, um, which kind of competed into the best or most used front-end framework. Now Astro goes, from a different angle. When they try to make the client bundles as small as possible, because Heavy Next.js or Gatsby sites come with a pretty bulky client size bundles. Right. Um, so Astro is trying to do it the other way to, to include as low JavaScript as possible for the website to work. Now with Astro, you can work with, uh, with React with Vue.js, with vanilla JavaScript, whatever you like. The important thing is that it gives you the frame for, for the website and you can include whatever you need, whatever you want. Right. It's as simple as that. Um, otherwise the pages are server side, uh, build and render. Uh, which is different from, from, uh, Next.js or it can be different depending on the page rendering strategy. Um, but the important thing is that Astro can work in the scope of serverless functions as well. Right. So you don't even, um, care all that much. Um, depending on what's our traffic on the website. But, uh, um, other than that, I stress a very good fit for a smaller, smaller sites. So that's Astro. Um, I'm going to share with the website in a second.

So let's see, what is the next step? So we've got up and running. We registered for the free plan. Um, you can also use my project. I'm going to. You know, add the API keys in a, in a second to the chat window. Um, and we called the CLI project an editor of environment. So this is the complete first step that we've already done at this point. Yeah. We have all the content in the CMS and we run the NPM run migrate. Um, so we should be good. Uh, next step would be to run the empty Astro side. So the first repository that I showed you, um, Now how do I bring Firefox back? There we go. So the first repository I showed you the react summit, 20, 23 workshop. Uh, this is actually an Astro site. So if you clone, uh, if you cloned it from the main, uh, branch, then you should be able to do the same thing I'm doing now, which is open the visual studio and, uh, look at the, the code here. So first thing we would need to do is run MPMI to install all the dependencies if you don't have that yet. Now I already installed those cause last workshop, I didn't do that. And it took me 20 minutes to, to install everything. Um, because of, you know, uh, there were, there were just too many modules. Um, and, uh, right now when I do NPM run dev, it should, uh, it should compile a site and let me visit the site at low close 3000. Okay. Could not import. Yeah, this is my mistake. Sorry. So let's remove that from the index page. Yeah. Let's remove that one for now. All right, let me see, but this should be good. I think. Yeah, this is good. So if you're seeing the same thing as I am right now, when you run the local host 3000, uh, you should see the homepage. There is a nice video playing in the background and, um, just a bunch of texts and a few destinations. Yeah. So this is the, this is the plain esoteric site. I can give you a tour around how it works. So, um, in the source folder, there is a pages folder, which contains an index dot astro. File. Um, this is, what's a astro uses to, to build, you know, your page structure. Of course, it contains, uh, all, you know, um, file-based routing, just like next JS or, or other, uh, frameworks, but that's not what we're going to be doing today. Uh, we just want a simple page.

5. Astro Layouts and Adding Pages

Short description:

Astro layouts allow you to include styles in components, with the option for global or local scopes. Code within the boundaries of a component will never make it to the client bundle, ensuring server-side safety. Variables like title and is homepage can be defined and made available to the front end. After resolving some 404 errors, the site is up and running. Two pages, homepage and flights update, need to be added.

Uh, there are, uh, layouts. So I have only one layout, uh, for the whole website, which contains all the header, uh, header items. Uh, it contains the body. And of course it has a main section with a slot here. So this is where the pages actually inject their content into. Uh, it has a menu, it has a footer, all the basics.

So, um, you know, we don't have to spend time, you know, working on the basics here. So, um, the important, uh, the other important thing is that with Astro, you can include styles right in the components. So here on the layout, you see, there are some styles, which are actually SaaS. So you can CSS or SaaS or a CSS, depending on your preference. And Astro will actually build it for you. And there are two scopes, either global or local. So you see, these are the styles only for this component, while these styles are going to be applied globally throughout the whole site. Yeah. So this is how Astro works.

The important thing here is every component has these three dashes at the top, this actually serves as a boundary. It means that all the code that you include here in the, in the top section, we'll never make it to the client bundle. So if you need some code to run on the server side, which is going to be all the code that we're going to need today, this is safe code because we'll never make it to the client bundle. Yeah. That's what these boundaries are for. Now the rest is just a, just a JS6. Yeah. So it can be an HTML, it can be you know, JS6 as we know it from React which makes it really easy to compose all the pages, but if you actually want to use some variables here, like title or is homepage, you have to define those variables. So all these variables are going to be available to the front end. Yeah. But if you do some operations here that require API keys and everything else, they will never make it to the client bundle.

Okay. So that's Astro. Now we're still getting some 404s. Let me see. Where is that? I think it's coming from one of the components. Yeah. It should be coming from here because there is poster. Yeah. So just remove that. Save it and we should be fine. Yeah. No more 404s. Okay. So let me know if you, if you're on the same page, if you can run the site, when you do the little change, I still don't know what happened there. Oh, there was a typo here. It's a newsletter subscription, there should be P instead of B. So let me fix that. And we can use the component later. Right. So let me know if you need more time, otherwise we can take a look at the next step. Not setting up locally, only watching in case I'm the only one to follow you. Oh, don't worry about it. That's perfectly fine. You can just watch if you prefer, or you can just try it on your own. It's fine. Right. So let's move on. Let's close the container CLI, we don't need that anymore. And let's take a look at the presentation for the next step. Okay. I have to minimize this. So in the main project run NPMI and NPM RunDev. That's what I did already. And we've got the site up and running and now we need to add two pages. That's a homepage and it's a flight update, flights update page. So, as I mentioned, this website is for, is for an airline. Oh, Moritz says, same for me. Got here a bit delayed because of work. And I'm going to watch. Okay. Perfect guys. Thank you for, for telling me that then we don't have to, you know, wait. So there are two pages, homepage and flights update page. The first one is already there.

6. Building Flight Updates Page and Importing Data

Short description:

To display flight updates on the website, we create a new page called flightupdates.astro. The page includes a layout and a component that shows a table of flight updates. To fill the table with data from the CMS, we rename the.inv template to.inv and provide the project keys. We also create a content.ts file in the lib folder to define the delivery client. We install the TypeScript SDK and the model generator JS package to work with content in a type-safe way. Finally, we run the generate-model.ts script to generate TypeScript types based on the API key and project ID.

As we just looked at it. So let me just add the subscription back then. Oh, newsletter subscription. Let's save that. And, we can add another page for the flight update. It's now in order to understand the flight updates. Let me show you what I mean. So there is in the CMS, there are flights, there are destinations, there are, aircrafts and everything that an airline would have. In the case of a flight updates, that's what you want to display on the website for the, for the passengers. So if we look at the flight information here, there are a bunch of flights and every flight actually has an aircraft that operates it. It has an origin. It has a destination and it has a bunch of statuses, yeah, linked items. This one doesn't have any, but there should be one that has some. Yeah. For example, this one has a status that the flight has been canceled due to severe weather conditions and it's a cancellation severity. Right. So this is what we're looking for. Right. So this is what we want to display on the, on the website. Now this is just an exercise to do, to understand how Astro works, right. So I'm just going to come back into the Visual Studio and add a new page called flightupdates.astro. Now we can just start building the page just like that and build the HTML structure. If we're here to just, you know, watch and do the page, then what I'm going to do is I'm just going to copy the page from the other branch. So if you want to, if you want to check out the whole thing as it's already implemented, there is another branch called solve. So I'm just going to take the page from the source of that. Okay. Let's take a look at pages and flight updates. Oh, this is actually very, very simple one. So you see, it only takes a layout. So every page, just like the index page has a layout where we define title, homepage, you know, whatever we want. This is not a homepage, but that's a default to false. So we don't need to care about that. And there is one component called flight updates, which currently shows only a table of the flight updates, right? Let me save that and save the page as well so that we can look at how it works on the front-end. So now it should be working, okay. So this is just a table, of all the flight updates, and its statuses. Yeah, quite simple page. Now what I wanted to do here is to fill the table with the data coming from the headless CMS, right? The first quite easy step. And that's step number three, actually. To rename the.inv template to.inv and provide the project keys. So that's quite an easy task. I already have the.inv file here and it already contains the project ID and the API key, right? And since I deleted all the data at the start of the workshop and imported everything again, the API keys should be the same, right? So I don't have to do that step because it's already there. Um, for you or for anybody who's watching the recording, there is an.inv.template file, so you can just add the keys here and set that as.inv. Now, in order to get the data from the CMS into your Astra project, there is one simple step needed and that is to create the folder within source called lib and put here, we can call it content.ts file where we actually define the delivery client now to understand what the delivery client is and how it works, uh, first, let me, uh, Install this actually I should have done this first, but. I've already done that file. So yeah, let's just install the TypeScript SDK first. So TypeScript SDK is something that we will need for any content to actually make it into the project. Let's clear that I'm going to enter content slash, uh, content dash AI slash delivery SDK. So this should be quick. So this installs the delivery SDK that is designed for TypeScript that will allow us to get the data from content. I'm also going to install another package that is called model generator JS. Um, now I hope that I can find the NPM name here. Yeah. Now what the model generator does, or you might actually already have it in package JSON. Let me check quickly. Yeah, we already have it in deep dependencies. So the model generator, what it does is it takes all your content types and generates TypeScript types from that so that you can work with content in a type safe fashion, right? So let's try to do that. Now I don't have to install it because it's already installed. And the script is actually already included here and generate dash model.ts. What this essentially does is it only creates folder, removes folder and generates everything, you know, based on the API key and project ID. There are some configuration options that are designed to fit your project. So however you want to call it. We're just going to need to run this project and first create the folder as well. So I went to code models even if it's types. It's just the habit and I'm going to add the script into package that JSON because that's, I'm sure that's the next step. Yeah, the script is under generated models and we're going to run it using TS node. Now, if you notice that the script is in type script, yeah, so what do you need to do is run it on the fly using a TS node? Because otherwise we'd have to compile it in Java into JavaScript, right? So we already have the TS node in the dependencies. So the only thing we need to do is just add a script. I like to call it regenerate models. And we're going to write as TS node. I think it's attributes as RC, let me see.

7. Generating Models and Creating the Delivery Client

Short description:

To generate models for your content types, add a script in the npm package.json and run it. This will generate all the models in the content in the models folder. You can work with the generated models in a type-safe fashion. Regenerate the models whenever you make changes to the content types in the CMS. The generated project file contains metadata about your content types, allowing you to use constants instead of string literals in your code. The content.ts file is used to create the delivery client, which is necessary for accessing the content in your components. The delivery client code can be found in the GitHub repository of the SDK. The environment ID is mandatory when creating the delivery client, as it allows you to make changes to the content model without affecting the production environment.

I don't have it here. Okay. So let's see if I'm right. So it's in source, scripts and generate models.ts. Okay? So the only thing you need to do is add a script in the npm package.json and run it.

Now this is, okay. Once we send the file, looking for matching. What did I do? One extra quote here. And this is also PSM. Let's see if this is good. No. Requires argument, OK. Let me see in the backup, how it should look like. I think I'm quite close, honestly. Oh, this is, this is the model generator, okay. Wrong repository. Also wrong repository. Now I have the zoom banner all over my tabs. Let me just fix it here. No, no, this was the right one. Perfect. So package.json. Alright, it doesn't require the source attributes. So only provide the source. Path. So let me fix that. So ESM, and we actually don't need the quotes, it seems. Okay. This seems to work. Now, what this, what this does, it generates, it generates all the models in the content in the models folder. So obviously you need to run this for every time you actually add a new content type or adjust the content type in the CMS so that you have everything, uh, you know, fresh.

Now you see that under models, uh, we have, what do we have? Content types. Oh, I don't, I see them here. Custom resolver. Should be there. Content types. Oh, it's under content types. Okay. So you see here that we have now aircraft model, airport model. We were looking at flights, so let's look at the flight model. Uh, you see, this is an icon that item, but at least all the elements that are in that content type. Right. So when you're getting the data from the CMS, you're going to get it into in, in these types and, um, you can work with it in a type of safe fashion. I'm going to show you that in a second. Um, but yeah, as I said, it's important to regenerate this every time you change something and also generates a project file, uh, that I want to show you that contains all, uh, all the code names and all the other data about your content types. So these are the metadata of, uh, of your, uh, content model, right? The, the great thing here is, or the great benefit is that you don't have to, um, ride the code names as string literals. And, um, if, if there, if there even is a typo or anything like that, um, your code will be a more robust because you're just using the constants, right? You're not using the string literals. Same thing goes. If you change your code name of a content type, it will be reflected here when you regenerate it and you can work with it that way. So no more string literals in your code. And it's also marked as const, which means you can never change it. So throughout the whole lifespan of your application, this will remain the same. So that's what you also get generated. Let's move on and let's move back into the content.ts file that is used to, uh, create the delivery client and let all your components use it.

The first thing you need to do is do the delivery client or we actually need to export it. Um, and we're going to need to create it. Now, the best place to, uh, to find the code that actually creates this. If you don't know it by heart is the GitHub repository of the SDK. So I don't actually know it by heart. I could probably try, but. Okay. It's okay. So this is one delivery SDKJS. No, we already, have it installed, so we don't need to install it again, but, uh, we're gonna need to create the delivery client. So this is the code that we'll need, right. To create the delivery client and note that there is only one thing mandatory. And that is the environment ID. Now, why it's not called project ID. Right. The thing is that when you're working with a project and there is content inside, you as a developer, when you're trying out new features or you want to adjust the content model somehow, you would need to do it on production or have multiple staging environments.

8. Working with Environments and Fetching Data

Short description:

With content, you get environments by default. You can clone and switch environments, making it convenient for developers. In Astro, import the project ID and create the library client to use it in any component. This flexibility is not easily achievable in platforms like Gatsby or Next.js. The content.ts file exports the delivery client instance. The next step is to fetch data from the CMS and render it in the flight's update component.

With content, you get environments by default. So we're working with environment ID, and when you want to try something, you can just easily clone that environment into a new one. Use that one for playing or do the migration there. And then you can either switch those environments or mark the new environment as production. So it's mainly for developer experience.

Now, the environment ID is coming out of our dot net, dot env file. So let me see how we can do that in Astro. I think it's import meta and here we have the content API. Not the API key. That's wrong. Should be project ID. Of course, we need to add the import. That's why it's complaining. But now when you have the import here and you create the library client, you should be able to import the library client into any component that Astro uses. So by doing this, you can use delivery client and content delivery anywhere in your project, which is very handy. Not something that you can do easily on other platforms like Gatsby or Next.js.

So this is the content.ts file that we have in the third step. And it should export the delivery client instance, which it currently does. Now, the next step would be to edit the flight's update component and fetch the data from that data from the CMS. Now we already have the HTML structure ready, so the only thing we need to do is get the data in the scope of the component and render it.

9. Fetching Flight Updates and Setting Up Display

Short description:

We switch back to flight updates, astro, and define the flights we want using the delivery client. We set the depth parameter to include linked items and use the toPremise function to call the API. In the HTML structure, we make the flight updates dynamic by mapping the flights data items. We retrieve the flight elements, such as aircraft, origin, and destination, and set up the date and time using AJS. The flight status component handles the selection of the latest status and its class based on severity.

So we're just gonna switch back to flight updates, astro. And this is the place where we want to do it, right? So let's call that flight updates. And as I said we're going to use the delivery client, and you see it as the import automatically. And now we just define what we want, right? So we want items, we want items that are the flights. So my model, or my type, is called flight model, and you see it has all the fields, aircraft, arrival, departure, destination, origin, and statuses. So we're gonna take that.

We of course need to specify a type as well, which again comes from the const, as I was showing you a second ago. It comes from the content types. So as the code name, and then we need one more thing, and that's a depth parameter. Now this means we're gonna not only take the items themselves, but we also want the links items, because we have aircraft, we have destinations there, and we probably don't want only their code names, but we also want their titles, right? So we can display it in the table. I'm just gonna set the depth to two, which means the main item plus the one item that is below it, you know, in hierarchy. So we're gonna get that, and then we need to do toPremise, and this is an asynchronous call, it calls the API, so we want to await it. As I mentioned before with Astro, when you define a const like this and it holds data, you can use this const in the HTML structure.

So the flight updates, we have them here. The table is fine, but we're gonna have to make this part dynamic, because this is only one line. So what we're gonna do just like you would do in React, you just do here flight updates, was it flight updates? Yeah, flight updates, or these are actually flights, not flight updates. Let's call it flights. And then put the line in there. And now this is just a manual work. Okay, not just flights map but flights data items, because this is actually the response from the CMS, not the actual data that are in the data and items properties. Now, this is just a manual work, right? We just need to do flight dot elements. Now, this would be the name of the flight, which is actually system dot name. Because when you look how it's added to the CMS, the flight code is actually set up as the content item name. So that's why I'm using the dot system dot name. Otherwise, we're gonna be always using the elements. But this is the only special case. Let's go back, and let's do the rest. This is gonna be flight elements. I think this was the aircraft. What was that? Aircraft. Yeah. Aircraft. We probably want the lint item. And lint items are always an array, because there can be multiple items. Right now, the aircraft is only one, so I'm just gonna use the zero index. And we're looking for an ID of that aircraft, probably. We can use the ID or the type, right? We could probably do the type as well, just to make it more interesting. So let's do type and name. Now, for destinations, it's gonna be very similar. So it's again, flight elements. Or this is origin, actually. Origin. Again, we can have multiple origins, or we can have multiple lint items, but only one origin, right? It's actually enforced on the CMS level. You see that on the origin, there is exactly one item, enforced only one item. The same goes for destinations. So if you have that in draft, you can add as many as you'd like, but the system wouldn't let you publish it. So in order to publish an item, you have to comply with all the rules that are here. So when I'm preparing the code for the frontend, I know that there's gonna be always exactly one item.

Okay, now for the origin, we want the title. The same will go for destination. And then we need to set up the date and time. So that is going to be the ATS. So we're using AJS for that. And it's going to be flying elements and Departure. And we need to provide the format, so that's, I'm just guessing here I hope that I'm doing it right. Hour and minutes, we'll see if that works. And just copy that to the arrival. And the last thing is actually the status update. Now this gets a bit more complicated, because there can be multiple statuses as linked items, if the statuses are coming from the gate agents or whoever adds them in, these can be, there can be multiple statuses. So what we wanna do is you wanna select the latest one, the newest one, yeah? And there is also one other component and that is the class because then that reflects the severity of the information provided. Now we already have a flight status component here that handles all of that, all of that for us. It doesn't like my modules. Let's see if I remove that and import it again. Cannot find module. Okay, let's see if it's still, oh, now it works okay. We'll just catch issue probably. Right, so this component actually does exactly that. It's orders all the statuses based on the date and selects the first one in the list. So we don't have to worry about that. It's already implemented here.

10. Using the Flight Status Component

Short description:

We only need to use the flight status component and provide it with the data. Set up an interface called props in Astro to define the required props for the component. The flight updates table should now be up and running. Let me know if everything is clear or if I need to explain further. We can run the site with npm run dev.

So we only need to use the flight status component and provide it all the data. So flight status, and let me see, actually. I'm just gonna import it. Let me see what the data should be called. So it's called statuses, the attribute is called statuses. So in Astro, what you can actually do is set up an interface called props and define what props are required for that component, right? So whenever you do this, it's already typed as a status model. And it also gives me a hint when I do this. You see there is a statuses and it accepts the status model array. So I'm gonna do that and provide the fly elements, statuses, link items. All right, and that concludes the first step. So now the flight updates table should be up and running. Let's see if I can run this. Going to load this. Where's my page? Here, yeah. Perfect. So now you see these are all the flights that we have in the system. There are a bunch of nodes here. Some of them are problems, some of them are information and so on. So the data seem to be coming from the CMS correctly. Perfect. Now, another step is finished. Even if you guys are just watching, then let me know if everything is clear or if I need to explain something further. I know the project quite well, so some concepts may be easier for me. And, yeah. So let me know. I'm looking at the chat here, so that's okay. And then we can run this site with npm run dev, so let me know what you have done and everything seems to be working okay.

11. Adding Rich Text Resolution

Short description:

To add rich text resolution, we need to understand the challenges it presents. CMSs handle this differently, as do programming languages and front-end platforms. Resolving links, images, and inline components requires specific handling. By adjusting the content model, we can allow for rich text with components and images. We can link to other content items and resolve them in the front-end code. Publishing the changes may take a few seconds to propagate to the CDN. To display destinations, we use the Delivery Client to retrieve the destination model, including linked components. We can then map and display the data, including images and text, using Astro's rendering capabilities.

So the next thing to do would be to add a rich text resolution. Now, why is a rich text resolution a problem? When you look at the, let's go to the homepage. At the homepage, you see a bunch of components here and there are destinations. Now, currently, I have all the destinations in scope of the listing component and it just lists all the items just like we did on the flights update page, right? These are just a bunch of HTML elements in the listing component. You see our destinations, London, San Francisco, Stockholm, this is just a simple list.

Now, the thing is when you look at the data in the CMS, like London, for example, let's look at London. That's a destination, so we can filter that, let's look at London. You see that there is a photo, there's a title and there is a text. Now when I create a new version of that, I can obviously add more things, I can add more text. It's in Amsterdam though. And I can just format this somehow, H2 or whatever. And I can also add different things. Now, these are all disabled right now because when I imported the data using the CLI, these all items are marked as disabled. But what I can definitely do is go back to the content model and adjust the destination. So this is the template for the destination. And I can change the limitations of the rich text so that we allow not only text but we also allow components and items or images. When I save this and go back into content and look at our London, now what I can do is add inline components. I can add any because I didn't do any restrictions. Now we're gonna do that in a moment. I'll create a new component and do that. But I just wanted to show you that the rich text resolution is a bit of a problem because first of all, every CMS handles this differently so that when you need to resolve that, you have to learn the specifics of your CMS. And then in every language, I mean programming language in every front-end platform, this is handled also differently. So that's why it's a bit of a challenge. Now with Text, it's easy, with H2, H1 and so on. It gets complicated when you start linking other items. Like let's say, I want to see, checkout, Also Stockholm, right? And because this is an LCMS, not normal CMS, you can't work with URLs directly. So it's much better to just link for another content item. And that could be Stockholm, right? I did Stockholm. Now in your front end code, now you have to resolve this because it only links to another item. It only gives you the codename of the other item. So you have to resolve that link into a URL if you're on a website. The same goes for images. It same goes for inline components, yeah? If I add here inline component, in the code, you're gonna get a codename of that content item. And you have to resolve it into an HTML of that component. So that's what content resolution is about or Rich Text Resolution. Now let me publish this. So we get some data that we can work with. And in the meantime, because it usually takes a while between publishing of the item and the item actually being available on the CDN, because it's, you know, the design of the system is to be as resilient for the system to be as resilient and robust as possible. So there's a lot of message queues and everything just to make sure that the content is safely stored there. So it usually takes about a few seconds for it to arrive at the CDN. But what I wanted to do in the meantime is just do this. So putting your destinations, again, use the Delivery Client to get the destination model and make it type Destination. Now, again, we want the depth because we have the rich text there and we also want to get the components. Not only their code names, but we also want to get the data of those linked components. So I'm gonna do the depth two and do two Promise. So we're going to await it. And now we have destinations. So I'm just going to delete everything but the first list item. And let's call it Destinations. Again, data, items, and map. I'll just put this here. And of course we have that image here. So that should be easy. So we use the destination. Elements, and there is a photo, which is a Asset element. So let's use that. There is a value and there is also an array of elements because in the scope of one asset you can have as many images as you want. So that's why I'm selecting the first one because it's required. And there should always be at least one and the same will go for the alternative text. So elements, photo, value, zero, and description, right? So here, when you look at London, I think that for this I don't have any description. So I can say this is the our bridge, and updated. Now we're also going to have the alt attribute filter. So that's how you work with assets. Now it's required, so we will not be able to publish it unless there is at least one image, yeah? And we can do the rest. So this is easy, destination, elements, title, and value. And this gets a little bit tricky because now we have the paragraph here, but by default, and that is for only, by default, you get the content and the inline components in a form of HTML content, right? So if your content is simple enough, that means texts and bullet points and only these little easy things, what we can do is do, I think it's a fragment and set html. Now we can provide here the destination and element text. So this is astro way of rendering. This is basically like, like, you know, from react dangerously set inner HTML. This is the same thing, but just for astro.

12. RichText Resolution and Portable Text

Short description:

When we do fragments of HTML, we provide them the data and it renders that as HTML. Every Headless CMS is responsible for turning their content into portable text, removing complexity for developers. Portable text provides a defined structure for data or components, making it easier to translate into a website. Custom blocks, such as images and code, are defined, while components are referenced. The task is to transform the RichText resolution into portable text.

When we do fragments of HTML, we provide them the data and it renders that as HTML. So when you look at the page, I think this is still running so we can just go to home page. Yeah. Oh, maybe it was already there. So now you see there is London, San Francisco, Stockholm. There's the react summit is in Amsterdam, though. It's an h2. So you see, also the h2 is preserved here. We, we didn't even do any resolve, resolving, but it's still there. And there is check out also Stockhom, but you see this doesn't lead anywhere. It still goes to the homepage. So we would need to resolve it first. Yeah. You see, there is a data item ID that contains the, the ID of the other content item, but the Zeta CMS doesn't, doesn't have any idea of where that should lead or what is the URL of the other item. So it's your job on the front end to actually resolve that. But so far so good. So we have all the content coming from the CMS. All the destinations are visible. So let me get back to this, to the presentation for a second, and this is a good time to actually, to show you what the portable text is about. So, as I mentioned, every CMS handles their, the restriction solution a little bit differently. So what it typically, what typically happens is that you have a headless CMS, it gives you the data in some form. This is a blue circle, for example. It could be Jason, could be, could be HTML, could be XML, could be anything really. And also the data are in different formats, even though two CMSs use Jason as their export structure, the structure of the data will be different. So what do you do then, is you take the data and you render it on a website. Website can be in JavaScript, can be in.NET, can be Astro, can be Svelte, can be anything that you can think of. The complexity that this generates is that you have pretty much infinite number of implementations, because you first need to take into account the structure from your Atlas CMS, then you need to enter account the possibilities and tools from your platform, and then you do the integration. So you as a developer have quite a lot of work to do, or you can find a package that does that already. But the problem is, when you switch the CMS or you switch the platform, you're still facing the same issue again. That means you have to take all these things into account again and fix the code so that it does what it should be doing to resolve everything, right? What portable text does is it tries to solve the problem of the Headless CMS or on the Headless CMS part. So what it does is that the CMS provides a plug-in or a way to turn the data into portable text. So that's defined structure of the data or of components of blocks, pretty much blocks of content. And then only these blocks are translated into the website. So when you have data in the portable text, we can use a code that will display them on an Astra website. Now, what this definitely does is it removes the complexity that would be otherwise on the developers. It removes the complexity and moves that to the Headless CMS side, because now every Headless CMS is responsible for turning their content, or your content, but in their structure, into the portable text. And even if you change the Headless CMS and the Headless CMS can deliver the data in the portable text format, you're quite covered because the syntax and the way of how data are displayed and delivered is defined, right? So that removes the complexity of the first part, and then from translating the portable text into the website, you don't have to do it on your own. There are already packages that do that because you have a single source and a single target. So it's easy to make that transition from defined format into a defined framework. So you don't actually have to do everything on your own, but we can use a GitHub libraries that help you do the same. And as both of these are standardized, it's much easier to make that package and to maintain it. So that's portable text. I can show you the GitHub repository of portable text if I have it open. So let me find it here. So we want only the main repository. Now let me just paste this into chat window. I don't think it's in links. So if you want to check it out on your own, check it out. So this is the main repository for the portable text or it's actually the organization. We have to check this one. So it's portable. That's portable text again. So we need to see how the portable text actually looks like. So the most important thing is, oh, let me see. So there is like, this is one block for example, or two actually. So there's one that is called span text. There's another one that has a mark emphasis and is important. So this is how the portable text kind of works. What's more, like these are the basics. As I said the H1s, H2s, emphasis balls, italics and so on. What's more important or interesting, are the custom blocks. So I showed you that we can actually link to other content items. We can also add inline components, which we're going to do in a moment. But what I wanted to show you is that there are custom blocks of type image, of type code, there is another one of type custom or component, but these are all defined, right? This is not something that we can say, hey, this is going to be a type car. This is going to be a type computer. Now these are all defined, so that there is some kind of standardization for type code, for type image, but for everything else, there is also a type component that will contain a reference. So this is exactly how it use case for the inline components but I'll show you that in a moment, right? But just so we know that this is the defined structure of the content that is coming from the HeadlessCMS and that you need to render. So that's what portable text is. And the task now is to transform actually the RichText resolution as we have it now in the Astra side into using the portable text. Now, first of all, the first part is to actually turn the RichText output of the Headless CMS or of into portable text.

13. Working with Portable Text in Astro

Short description:

To work with portable text in Astro, we need the RichTextResolver package and the Astro Portable Text package. We use the transform to portable text and NodeParse functions to parse the HTML tree and convert it into portable text. The portable text component is used to render the portable text into Astro components. However, custom blocks and inline components require additional handling.

For that, we're gonna need the first package MPM or the RichTextResolver. So let me install that. Let's start this. RichTextResolver, I hope this is correct. Okay, let's wait a second to install everything. And when we have that one, we're gonna need another MPM package.

So let's say now we transformed the content into the portable text with methods available in that package. The next step would be to render the portable text into astro components. There are multiple ways you can do that. If you're using React, there is a library for portable text to React. If you're using simple HTML, you can use portable text to HTML library. In this case, I want to render it into, or I want to turn it into astro components. So every content type that I have on the CMS will correspond to one astro component that I have locally.

So for that there is an npm package for portable text to astro transformation, which is by Richard Shackelton. It's currently, it's actually one of the content.AIMVPs. And the package is actually very young. So it's probably not production ready yet, but just like not too many people are working with portable texts yet because this is relatively new technology or new standardization. Then the package is also quite young, but let's install that. So npm i slash, what's it called? Astro portable text. Okay. Now, while this installs, I'm just gonna remove the fragment here because we're gonna need to do a little bit more than that. And this is the last part here actually in the presentation. So we're gonna use the portable text component that comes from this library. And we're gonna use these two functions transform to portable text, plus Node parse for content. I'm gonna explain both of them in a second. So, let's use the portable text component. And he's not gonna like it because I just installed it. And for some reason, I always have to close the window and start it again. So let me just do that. Should be a second. Okay. So let's use the portable text component. No, still nothing. I don't know what's happening. Sometimes Visual Studio is really stubborn, but that's fine. So let's import portable text from Astro Portable Text. Did I make a typo somewhere? Oh, yeah, it could be. It's not. Okay. And there are three things we need to provide. And you can look, at the auto complete what it suggests. So that's a value. That's a components. And that's a context. Now, the context will be different for different CMSs because the content is a bit different in a way it handles linked items. So that's always going to be different from CMS to CMS. But value is actually the rich text tree that you get from the CMS, right? That's what I showed you the structure that is defined by portable text. So as mentioned in the presentation, I'm going to use these two methods. Transform Two Portable Texts and NodeParse for content. So first of all, we need to use the NodeParse. And let's see if this is going to work. No, it's not. Okay. So I'm just gonna do the import myself. So NodeParse and we want to do that from the ContentAI rich text resolver. So NodeParse and we need to input, so that's gonna be the destination, elements, text, and value. So why we are doing that? As I mentioned before, the elements text, so the rich text that is coming from the CMS is in an HTML form. So what we need to do, we need to first parse the HTML tree to get all the blocks from it. So that's what the NodeParse does. I'm using NodeParse because I'm working with Astro and we're doing it server side. If we were doing it on the frontend, there's also a browser parse, also in the same package. So depending on where the resolution takes place. And then the next thing is to do the transform to portable text of the parsed tree. Of course, it's the same import. Okay, now we have the value available. This is all we need to do for the value, right? We already have the portable text. Now the problem is for our- let me just switch back quickly to the portable text. Oh, here it is. The problem is when we have a custom block like the custom inline components, but this is what we have there will already work for the H1s, H2s bullet points, and everything. The problem is with the inline components and everything else that is linked that these are considered custom blocks.

14. Portable Text Transformation and Component Context

Short description:

The transform to portable text only provides a type component and a reference with the code name of the linked component. To render the component, we need the whole context of the linked items. By setting the depth parameter to two, we have the linked items already downloaded and can provide them to the context of the portable text component.

And what we get from the transform to portable text is only going to be a type component. And there is going to be a reference. The reference will hold the code name of the linked component, right? That's not really helpful because we need to render the component not only know its codename So for that, we're going to do one more thing. And that is provide the whole context of all the linked items of that specific reach text field. So the context will be destination elements, text, and linked items, right? These are because I defined here the depth parameter to be to be two. So we have the linked items already downloaded, we have the data in there. I would just need to provide it to the context of the portable text component.

15. Handling Custom Components in Astro Portable Text

Short description:

To handle custom components in Astro portable text, we need to create an Astro component and register it as a resolving component. The type 'component' is used for custom code components. We create a portable text component to handle all dynamic use cases. The context function transforms the array of linked items into a key-value pair object. The portable text component renders the portable text and the context is used to provide the necessary data. After importing the necessary files and making the required adjustments, we are ready to run the project.

Now it's complaining because I just provided a simple array and it expects key to value kind of structure, yeah? So we're just going to fix that in a second with reduce. But I just want to touch on the components a bit as well, because by default, you don't have to do anything here. Yeah, you can just leave the components empty. It also says that it's optional, you see? Here, it's optional. But what we need to do because we want custom components, we need to provide here, I think it's component, let me check. It's actually in the other repository. Okay, let's find it here. Astro portable text. See if it will read me. So we're looking for, what is it, components, here it is. So this is how we can actually override the code. Let me just copy that. Astro types, I think I did it right. And I've explained it on, this. So here we're gonna do a component. And here we need to provide an Astro component. One that we'll create. Yeah, it's not there. There is nothing like Astro component, but we have to create one. What this does is it registers this component as a resolving component of that type. Now, as I mentioned, the types, you can't really make them up. I think they are defined somewhere. Oh, maybe I don't have the definitions here. But they are defined. And component is actually one of the names. And we're gonna provide another component, Astro component that will handle all the dynamic use cases, right? You saw in the documentation that there was code. So that is used for the code component, yeah? This is a custom component called component. It's called component because that's what the content... Let me just do it there. The content resolver calls them. Resolve. So here on the portable text resolution, you can see how it actually handles that transformation. And you see that the type is component, right? The type is component and it contains the reference to the link item or component code names. So these are the two key informations. That's why the type component, as I mentioned, this comes from the portable text. So the type component is what I have here in the code, right? So I'm saying everything that comes in as a component type will be handled by this component. Now, I'm gonna call that portable text component. We're gonna create that one. Let's put here a new file, portable text component. Okay, now I think we can import it. No, wrong import. Portable text component. We are under components, so we can do portable text component Astra. Now we should be good. So now it means that everything that is coming as a component will be handled by this component. And the context, let me just copy that from the soft repository. So I don't have to figure the radius right here now because honestly, with radius, it's always a bit of a pain to get that to work. So let's see it's here. Yeah, so that's it. I'm going to copy that over here. Now you see that essentially what this does is let's add the imports. What this does is it receives the array of linked items and turns it into portable text as to context. That is a key value pair. There's really nothing more to it. It's just the key value pair. And what it does is it takes every link item and uses its codename as a key. Yeah. So it releases that into object item system code name equals item and returns that. So this is actually what the portable text wants. So let's use that function. Transform linked items to global context. And that's good. Now let's look at the portable text component. Let's put there hello world so that we see something is happening. And I think we are ready to run the project. All right. I'm able to render Mark component because it's undefined. Okay. This looks like there is a wrong import somewhere. That's probably going to be this.

16. Troubleshooting and Adding CTA Component

Short description:

There was an import issue, but it was resolved. The problem was related to the link, which required the addition of the mark component. After making the necessary adjustments, the resolution started working. As a final step, a CTA component was added to include a call-to-action button for the destination. The content model was edited to create the CTA button component with text and URL fields. The destination was also adjusted to use the new component.

So there shouldn't be the source code involved. Portable access for contact as no exported member. Okay. Now this is definitely an import issue. Just wonder, I think this is correct, actually. You guys have something wrong there. Let me see. Let me see. It's here. Portable next as contacts, now it's here. So the import was actually okay. That's my mistake. So it must be somewhere else. Okay. Let's fix this. Let's fix that. Rich text resolver. That's okay. This is also good. So what's there not to like? Mark component, because it is undefined. Okay. I haven't seen that area yet. Render component, I'm going to render mark component. But it typically is an import issue. So let me check if everything is good here. It's still portable text. That should be good. Okay. Maybe it doesn't like that I already find the component. Let me see. I think I used the same before. Yeah, that should be good. Let me see if I have everything the same. This is all good. Let's try to exchange this one by one. Let's try this runtime. Okay, so it's somehow it's inside. Let me just run this again. The typical developer fix, you know, just restart everything, hoping it would work. No, something, he doesn't like something. I'm able to render displaying this component. Did you forgot to import a component or is there a typo? No, we definitely imported a component. Maybe he doesn't like my new component because I didn't do the boundaries. Let's see. No, that's also not the problem. Oh, that might be because we have the link in there. So in order to, for the links to work, we would have to add the mark component because that actually translates the codenames into real links. Let me see if that is the issue. So I'm just going to, no, no, no, don't unpublish in archive. I'm just going to create a new one. So let me remove that and publish it. Obviously, if it cannot do the resolution of the link because it doesn't know how my URLs look like. So if that is the issue, it should start working now. It takes a second before we get the new data. There we go. So it was the link. We don't have to provide the mapper from the codenames to URLs, but otherwise the resolution seems to be working. Now, what I wanted to do as one last step here is to add a CTA component. CTA, that we can include a little button that says that it's a call to action for the destination, pretty much. So it's a content type. So I'm gonna create a new content type with text and URL and adjust the destination to be able to use that component, right? So let's just switch to the CMS for one last time and edit the content model and create here a new component. CTA button. Let's put their text. And we'll require it. And let's put here a URL. Let's require that one as well. And let's match a specific pattern. It should be web URL. Right? I'm just gonna save the changes here and save the content type. Now, the next step would be to go into the destination, and actually, we already allow some of the components in the rich text field, but what I want to do here is just to make sure that all of the available options are implemented.

17. Adding Inline Components and Rendering CTA Button

Short description:

We switch the allowed content types to only contain the CTA button. We create a new version of the destination, London, and add inline components for and Org to Amsterdam with We publish the changes and see the two inline components rendered as hello worlds. We regenerate models and adjust the portable text component to render the CTA button component. We find the item using the Rich Text Resolver and the component's ref. We retrieve the linked item by its code name from the global context. Finally, we render the CTA component if the linked item's system type is a CTA component.

Yeah? So, I'm just gonna switch here that the allowed content types contain only the CTA button. Yeah, nothing else. So, gonna save the changes and jump back into the destination, London. And let's create a new version. And let's put here the linked, or the inline component, right? We can do that. We can do images. We can do existing items. We can do inline components. So inline component will only live in the scope of this item. While linked item would be available or would be available for reuse. Yeah? So, you can go to London today, for example. Let's put here And this is actually our component. We can write as many as we like. Yeah, let's, let's add another one. Let's call that Org to Amsterdam. I don't know what's that. So let's do Nothing better comes to mind. So, we have two inline components here. Let's publish that. And if I didn't make a mistake, then in a moment we should see both of them here. And let's call that as hello worlds, right? We only did the hello world in the scope of that component. So, now we have two hello worlds, right? So it was rendered through the portable text into Astro component and now it's, it, it reads hello world. So the last part of that is, well, first of all, as I mentioned before, whenever you do a change in the content model, you have to run regenerate models. And actually on our own site, we run it every, every bill. Yeah, so every bill gets a fresh set of types in JavaScript. And now when we have everything in place we can actually adjust the portable text component because here we need to do one thing and that is decide which Astro component will be rendered. Because we don't want one component to handle all of the HTML cases, right? We want to add new component that is called CTA button or CTA. Actually I already have that in there. So I don't have to create it on my own but you see here CTA Astro doesn't have a component CTA model because right now I called it component CTA button model. So let me fix that here as well. But this, what this does, this it only generates a simple link in HTML, right? It has a URL, it has a text. It has some kind of a styling here. And it has actually, this is not needed. It will have, as I showed you before interface props and it will take data which will be a component CTA button model, right? So here we say that this component expects one attribute or one property, one prop called data which is of type component CTA button model. And then we only need to reassign it, right? So we say elements is Astro.props. Of course this cannot be, what's wrong? Oh, sorry, element props data. Cause this is the data and elements is one level below but here we only need elements.url and elements.text. Now you can also do to the URL and text just to make it a bit more nice and do it this way and remove the elements as well. So this is essentially how we want the Astro components to look like. And when this is done, we can go back into the portable text component and here, based on what we receive, here we can say this is gonna be the CTA component. Now, in order to do that, we need to find the item. Now, this is coming from the Rich Text Resolver. Now, I already closed that repository, so let me just copy that as well from here and I can explain that. So that's this part. So let's gotta copy it here. So what the resolver that takes the portable text and renders it into an Astro component, what it gives us in the scope of the portable text component is a large context of everything that's available. It's under Astro props and portable text. So it's just another property of the component and it gives us mainly the ref of the component. Now, let me show you how that works. So when I showed you in the text resolver that this is how the component looks like in the portable text, right? It has a underscore type component and it has a component that contains ref as a code name link to another item to that actual inline component. Now, this is what we're looking for because we also get in the global context, we get all the linked items that were available to us. So we know that the data are there somehow, we just need to get the code name. So we're getting the type and we're getting the ref in the component. So here you see we are getting from astro props portable texts. So those are all the data that we get from the resolver and we get the value, which is the block itself. And we're looking at that component that underscore ref. So that's component underscore ref, right? So this is the code name that we're looking for. And when we look back in the project, we are looking inside the global context for that key. So as I showed you before here, when we were transforming the linked items, we said that the linked item will be available under its code name. So the object key was the code name. So this is exactly what we are doing in scope of the portable text component, that we are looking at the context and finding the linked item by its code name. And of course the rest is just a reference to the delivery SDK. Yeah, the first one, the second one as well. And when we have that available, the last part is to actually do the front end. So if the linked item system.type equals content types, as I mentioned, we have everything in constants automatically generated from the CMS. We are looking for CTA components. So that's the first one, component CTE button and code name. In that case, we render the CTA and provide its data.

18. Resolving Linked Items and Conclusion

Short description:

To resolve the linked item data in the CTA button component, we cast it to the CTA button component model. After saving and running the project, the hello worlds are replaced with buttons that link to and Google. Portable text provides a unified way to handle rich text resolving, even across different platforms. The workshop is now concluded. Feel free to reach out on Twitter or meet at the React Summit in Amsterdam. Thank you for joining!

And the data is actually the linked item itself. We will likely need to style it first, or not style it, sorry, but we will need to cast it to CTA button component model so that the component knows that we are giving it the right data, which we are sure now that we are. Because we're looking for the system type component CTA and we need to add the import of CTA astro. So this way we can add as many components as we like here. We started with the CTA button, but we can do any more of these. And when I save that and run the project again, we should see, instead of two hello worlds, we should see two buttons here. So go to London today, which goes to and or to Amsterdam, which goes to Google. So this is how the portable text resolving works. Of course, it takes a bit of implementation work, but the good thing is that even if you need to do the same thing in Links.js or another platform, you can still do it the same way and get the data in a unified way.

Perfect. So this is what the portable text looks like and how it works. Of course, everything is in the solved branch of the workshop repository. I think we did all of that, added a new component, regenerated types and got the linked item. And now we check the resolution and it works with npm-rundef. So good job. Astro, it's actually nice to work with. We got the rich text resolving to work using portable text. And yeah, all the other features are at your disposal. So this is actually the end of the workshop. So thank you guys for watching. If you have any questions or you want to talk about Astro or portable text or anything that is related to the topic, then you can find me on Twitter. The QR code actually takes you there. Or we'll see each other at the React Summit in Amsterdam in a week and a half. So let me know if you have any questions. Otherwise, it was great to have you here. It was fun.

Watch more workshops on topic

React Summit 2023React Summit 2023
71 min
Building Blazing-Fast Websites with Next.js and
Workshop Free
Join us for a hands-on workshop where we'll show you how to level up your React skills to build a high-performance headless website using Next.js, Sanity, and the JAMstack architecture. No prior knowledge of Next.js or Sanity is required, making this workshop ideal for anyone familiar with React who wants to learn more about building dynamic, responsive websites.
In this workshop, we'll explore how Next.js, a React-based framework, can be used to build a static website with server-side rendering and dynamic routing. You'll learn how to use Sanity as a headless CMS to manage your website’s content, create custom page templates with Next.js, use APIs to integrate with the CMS, and deploy your website to production with Vercel.
By the end of this workshop, you will have a solid understanding of how Next.js and can be used together to create a high-performance, scalable, and flexible website.
React Summit 2023React Summit 2023
154 min
Localizing Your Remix Website
Workshop Free
Localized content helps you connect with your audience in their preferred language. It not only helps you grow your business but helps your audience understand your offerings better. In this workshop, you will get an introduction to localization and will learn how to implement localization to your Contentful-powered Remix website.
Table of contents:
- Introduction to Localization
- Introduction to Contentful
- Localization in Contentful
- Introduction to Remix
- Setting up a new Remix project
- Rendering content on the website
- Implementing Localization in Remix Website
- Recap
- Next Steps
Remix Conf Europe 2022Remix Conf Europe 2022
162 min
Crash Course into Remix & Storyblok
Workshop Free
You may read already about Remix. You probably already used it, and recently you may hear a lot about the headless CMSs. In this quick course, we will put all the pieces together, and I will show you why Storyblok in combination with Remix is the best combo for your next project. Stop by and try it yourself!
Table of content: 
- Introduction to Remix, atomic design
the headless world
- Environment setup
- Creating pages and understanding how the dynamic routing splat routes works
- Future tips and Q
Prerequisite(s): Node.js installed, GitHub account.
React Advanced Conference 2021React Advanced Conference 2021
160 min
Building Modern CMS Driven Web Applications with Strapi the OSS Headless CMS
In this workshop, we'll build out a fully functional website and integrated blog with Next.js and Strapi.
Table of contents:
- An introduction to Headless CMS and supported architectures
- Getting up and Running with Strapi and Next.js
- Integrating Blog functionality into a Next.js app
- Deploying your Next.js and Strapi Apps Bonus
- Implementing content previews with Next.js
Basic React Knowledge Basic knowledge of Node.js and npm Basic Web Concepts.

Node Congress 2022Node Congress 2022
134 min
Deploying a decoupled restaurant review site to production with Strapi and
Workshop Free
Node.js has become an increasingly popular language to build and deploy backend APIs. In a world of legacy CMSs adopting decoupled implementations, plenty of frameworks have sprung up to classify themselves as "headless" CMSs, designed from the start to provide an easy way to personalize content models, administer permissions and authentication, and serve a content API quickly.
Strapi, one of the leaders in this space, has recently released their v4 version of the framework, and with it can be deployed alongside a number of frontends within the same project, giving a drastically simplified development experience working with decoupled sites. In this workshop, we'll deploy a Strapi demo application, which has been configured to serve a restaurant review site.
Piece piece you will add database services, tests, and frontends, all within the safety of isolated development environments. At the end, each user will have a functioning decoupled site, and some greater understanding of working with decoupled sites in production.

Check out more articles and videos

We constantly think of articles and videos that might spark Git people interest / skill us up or help building a stellar career

TypeScript Congress 2022TypeScript Congress 2022
10 min
How to properly handle URL slug changes in Next.js
If you're using a headless CMS for storing content, you also work with URL slugs, the last parts of any URL. The problem is, content editors are able to freely change the slugs which can cause 404 errors, lost page ranks, broken links, and in the end confused visitors on your site. In this talk, I will present a solution for keeping a history of URL slugs in the CMS and explain how to implement a proper redirect mechanism (using TypeScript!) for dynamically generated pages on a Next.js website.
Add to the talk notes: 
React Summit 2022React Summit 2022
21 min
Remixing WordPress: Building a Headless Site with Remix, WPGraphQL, and Web Fundamentals
One of the largest draws of headless or decoupled WordPress architecture is that we can endlessly remix the WordPress CMS that content creators love with the latest and greatest of web technology. This presentation will demonstrate how quickly a developer can get up and running with headless WordPress development using the Remix full stack framework. Using open source tools like WPGraphQL and Atlas Content Modeler, you can transform your WordPress site into a powerful headless CMS that serves data via GraphQL. We’ll see how we can query for and use that data in a Remix app, and also discuss how the framework’s focus on web fundamentals can benefit both the developer and the visitors who use our sites.
JSNation 2022JSNation 2022
23 min
The Next Wave of Web Frameworks is BYOJS
Web application development has had many shifts over the lifetime of the web. From server-side applications with a sprinkle of JavaScript to Single Page Applications built entirely with JavaScript. Now we’re heading back to where many new web frameworks build for static first, with JavaScript added as needed. This talk covers building web applications with JavaScript through the lens of Astro, a static site generator where the choice of JavaScript framework is uniquely yours.

React Summit 2022React Summit 2022
20 min
Large scale projects challenges (NextJS - Contentful)
NextJS is an excellent full stack framework. Contentful is a well-known flexible headless CMS. Together are a great match, but when we talk large scale projects the challenges are completely different than the ones you may face in a small to medium scale project. Leonidas will try to raise your awareness on such challenges based on Greece's experience on redesigning Vodafone's site to create beautiful self-serve and guided journeys for Vodafone customers.
GraphQL Galaxy 2021GraphQL Galaxy 2021
23 min
Exploring the WordPress Graph with Next.js & WPGraphQL
Headless Wordpress using its built-in REST API is a powerful solution to scale WordPress to the web, but complex relationships can easily turn into chains of requests, making maintainability difficult along with the potential cost of performance.
With WPGraphQL, we can harness the benefits of GraphQL, leading to a better developer experience and optimized request logic, making sure we’re only delivering what we need to our users.
We’ll explore these advantages and how these pieces fit together with modern tools like Next.js to build great experiences for the web.