Personalization on the Jamstack
Delivering personalized experiences is an effective way to increase sales and engagement — but it also sounds HARD. In this episode, Tim Benniks will teach us how Uniform makes it possible.
Links & Resources
Full Transcript
Click to toggle the visibility of the 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've got Tim Benniks. Tim, thank you for hanging out. How are you doing today?
TIM: Oh, good. Thank you for having me. I'm super excited to be on a livestream once in a while. Awesome stuff.
JASON: I'm very excited to be working with you today. I think we're gonna do some really, really interesting stuff, but before we talk about what we're gonna be doing, let's talk about who you are. So, for folx who aren't familiar with your work, do you want to give us a little bit of a background?
TIM: Yeah, sure. My name is Tim. I'm originally from Amsterdam. Now I live in France, so if you hear a strange accent, that's where this comes from. My whole career, I've actually worked at agencies, so, I've been building campaigns and ecommerces for usage brands. That was all good and fun, but I was always head down in projects going diehard for clients like Nike or Chanel and never actually showing what we did. Those brands are a little magical unicorns. Nobody needs to know how they do what they do. And that was all good and fun, but you kind of learn a lot about enterprise and things that are a little bit complicated and it's a little bit behind. Then you have companies like Netlify running, like, years ahead of them. And so recently, I turned more to the community, because I did learn a whole bunch of this work, but also there was so much more to learn from the community to me. And so now I joined a start‑up called Uniform, and I make YouTube videos and I stream occasionally, so my career completely changed. Now I'm here with you, and I'm super excited about that.
JASON: I'm definitely looking forward to it. So, you know, you mentioned that you're at Uniform now.
TIM: Yeah.
JASON: And Uniform specializes, if I understand correctly, in personalization. Is that right?
TIM: Well, up until about two weeks ago, that is completely correct. So, you have been briefed properly. However, we did change up a little bit. Like this is the interesting ‑‑ interesting thing about Uniform. We all come from the similar background as me, right? We are all in the enterprise, big projects, large‑scale, slow stuff, and we all knew it was gonna go into the direction of doing things with Jamstack, performance, edge handling, things like that, and so what we have to deal with are things called DXPs, digital experience platforms. And so what that means, it's like a whole bunch of tools together that make the digital experience for your end user, but also for the developers.
JASON: Got it.
TIM: Also for the marketers. Also for the data engineers or anyone in that whole group of things that works together is kind of the audience of a digital experience platform. When you build your blog, you don't really have this, right? You have a very small bit. But when you go to bigger brands that I used to work for, they had digital asset management, you know, CDNs or CMSs.
JASON: Right.
TIM: Lots of things together make up one big brand, right? So, we all worked in that and we all felt the pain of it didn't really work. So, that's why Uniform started. So, there was a bunch of product people that used to work at Sitecore, for example, that was one of those systems that used to do that. And they all stopped working there, made their own company called Uniform. So, what we started with was exactly personalization. Because that's inside the context of DXPs. Not the site itself. Everyone wants it. Really hard to do, so why not make a product that kind of sits there easily to use, integrate with new stuff like Jamstack and React and Vue and all of that stuff? And then go in there and make that work. But that was only the first step. Because we already knew there are many of those hatless things out there, right? You have CMSs, but you have personalization engines, you have analytics, you have ecommerce. There's so many hatless systems, it's really hard for users to actually use it. Let's say you're a content editor, you have to log into five things to make one page. It's really challenging. That's actually, our final form, we are called Uniform, because we want to kind of take all these sources, put them together in a really easy way, and make that work where developers are happy but also content editors are happy and also enterprise people, in quotes, are happy. So, personalization is a huge part of that because all these brands want this.
JASON: Yeah.
TIM: So, what I want to show you today is kind of this platform that we have and then focus on the personalization bit, if that makes sense.
JASON: Yeah, so, for folx who don't know what personalization is, because I think that's kind of a big umbrella term.
TIM: Oh, true.
JASON: When we start talking about personalization, what is actually happening? What are we doing on ‑‑ is it per page? Is it per website? Is it either? Both? How do you kind of approach personalization?
TIM: There's lots of approaches to it, and all of them are complicated, which is kind of strange, but it is what it is. So, basically, personalization is if you don't notice that it's happening, you are most happy and you have the best conversion, shall we say. Which means I click "buy" or I engage with something. So, what generally happens with personalization is you figure out what this user is doing, who they are, and you change the website according to the actions that they take. This can be on page level, component level, you know, query level. There are so many different little things. And this is where Uniform is actually quite different than the rest. Because when you look at traditional personalization, what you do is you fit people in sort of a bucket according to roles, right? You are a hat‑wearing geek, like us two, potentially, right? We fit in that bucket.
JASON: Right.
TIM: Then we have a certain geolocation, we have a certain affinity for JavaScript. What you used to do, traditionally, is make a whole bunch of rules based on who this user is and what they do and put them in a bucket and show specific content. And so lots of different rules happen because for different components on your page, you can make different rules, right? So, it's really hard. In the systems that are out there now that are not Uniform, you kind of have to have a PhD in rule‑making to figure out what do I show for this user when they're here and they did that? And so what you then see and you put people in personas to simplify it.
JASON: Mm‑hmm.
TIM: It's like the hat‑wearing bearded nerd is one. And then the other is like a sales person or whatever. The thing is both of these personas could have the same goal on the site. You make all these rules, you duplicate them. This is how Uniform is quite different. We just look at the intent of the user. What do you actually want on the site, right? And so we look at the actions a user takes, and based on these actions, like go to a page, click on a button, almost like a Google Analytics‑type implementation, right? Send some events. Then what we then do is we look at points. So, we score against specific intent or a goal a user has, and based on that score, we show different content.
JASON: Okay.
TIM: And the cool thing about this is it's all in the browser. There's no origin. There's no server we have to ask which rules apply to this person.
JASON: Interesting.
TIM: And that's actually very different. You almost create something that is, like, semi‑automated. It's kind of organic. Because when you click around the website and we track your behavior, shall we say, like, you go to a page where you clicked on, let's say, a Linkedin page that has a query parameter to it. That's a pretty good indication you're interested in that link. We score a little bit higher from that one. It would be you have a cookie from whatever, CRM system or something. We also score you with that one, but a bit lower because it wasn't your intention that you did that, you just had that cookie. When you go on a certain page and have a certain score against something, we grab the content that you tagged in your CMS and show that against that score. So, in the end, what we've built, you have a whole list of variations of content, and we just order that list based on the score.
JASON: Got it.
TIM: So, we have a very fancy list order. It's the simplest way we can say this.
JASON: You know, I think the power of a lot of these solutions is not in the way that they're implemented, it's in the mental models that we use to get there, right?
TIM: Exactly.
JASON: I think one of the ways that personalization feels very challenging is that, as you said, you have to write all these rules. Okay, so, if this person came from Facebook and is likely to be buying men's sizes of clothing and likely to, you know, live in the U.S., then we should show the, like, the homepage should reorder to show all of the men's products in USD. And those, you know, those sorts of ‑‑ and we'll add a Facebook friend discount code, like, available to first‑time shoppers or something like that, right? So, you have to build these rules. That's very simple if you have exactly one thing you're trying to accomplish.
TIM: It's always 20 things, right?
JASON: Yeah, and it's like exploding complexity, too, because it's not just 20 things, it's 20 variables. And, you know, if you look at the ‑‑ just the order of complexity between, like, one and two things not too much, but between, like, two and ten things, suddenly you've got millions of combinations.
TIM: Yep.
JASON: And that is ‑‑ human brains don't track that, right? So, what I like about what you've done here is that you're thinking ‑‑ instead of saying, like, this is a dude from the United States from Facebook, you're instead saying, this person might want a hat.
TIM: Exactly. It's very different, isn't it?
JASON: And that's a way different way to sell. It's less ‑‑ there's more ‑‑ there's more, like, trying to solve my problem and less trying to figure out who I am. And I also like that from a privacy standpoint ‑‑
TIM: Exactly.
JASON: ‑‑ because it changes the incentives for companies. We don't have to get ‑‑ I don't need granular data. I don't need to know what retail stores you visited because your phone was in your pocket and we can buy that data from Facebook.
TIM: Exactly. We don't care at all, to be honest. There is no need in this model. You just follow what the user does. Therefore, figure out what their intent might be. If you have granular enough content, it works.
JASON: Yeah. No, I like that a lot. And so ‑‑
TIM: No third‑party cookies. Won't store anything, if you don't want to. Which is very interesting. Sorry, continue.
JASON: No, no, that's good. So, thinking about this as a sort of ‑‑ what else should somebody know? I mean, to state what we want to do today, our intention is we're gonna use Contentful to set up some content.
TIM: Yep.
JASON: And then we're gonna build a website. I think our hope is to use Nuxt3. We'll probably start with Nuxt2 because we know that works. Nuxt3 just dropped today.
TIM: Exactly.
JASON: And then we're gonna personalize that with Uniform.
TIM: Yes.
JASON: So, just side of just jumping in and building it, is there anything we should talk about in this flow? Like, you know, is Uniform ‑‑ can I use it with any framework, like whatever I want to use? Or is it built for earn front end styles?
TIM: Well, now that we've dropped our latest version of Uniform, it's actually much more opinionless. There is not that much opinion of what you need, so you can kind of use anything that you want. Because we have an SDK that is basically just JavaScript. It's agnostic to anything. It just works. And so what I want to take you through today is actually we'll go to our homepage, and then we say build your stack. So, we'll help you to connect the dots of the different systems that you have. In our case, for now, it's simple, right? It's just the Contentful one.
JASON: Got it.
TIM: But once we do that, you'll actually go into an interface where you can select components that you set up as models. But you select them from Contentful. And what that means, you can just click "personalize this," and then you can just select another option from Contentful in our interface.
JASON: Got it.
TIM: And we'll show you when you're there, of course, but what that does is there is only one REST end point to this data. When we do some code, we'll query that REST end point, enhance some data and map to how you want to work with it, and then we can put it on Netlify.
JASON: Got it.
TIM: And so we've done it this way because if you wanted to switch Contentful for Sanity, you just do it in that interface and then the REST end point is the same. And that makes things very easy, right? If you want to add commerce to it, sure. Add an integration. Drag and drop some components from that commerce engine and they come into that end point. So, you kind of are agnostic from whatever integration you have if you code it the way ‑‑ and you can code it the way you want. And, of course, this is a little bit abstract when we talk like this, but I would love to show you and give you that feeling of just going through that onboarding, setting it up, and then having a look at how that all works, right? So, it's slightly bigger than just the personalization, because we now use that interface that we've built to really easily add the personalization. And luckily, our new SDK has this personalization business that you would normally code. It's kind of baked in so you get a Vue or React personalized component that does it for you already. So, it's slightly easier. A little bit more black magic, but we have a bit of time, right? So, if you want to learn how it works, we'll just dive in and I'll show you directly.
JASON: Let's do it. I'm going to flip us over so we can start to get our hands dirty. As we're jumping across, keep in mind this episode, like every episode, has live captioning. We've got Jordan here from White Coat Captioning doing all of that for us. It's available on the homepage if you want to read along. That captioning is made possible by our sponsors. We've got Netlify, Fauna and Auth0, all kicking in to make this show more accessible to more people. Which means a lot to me. As you are clicking things on the internet, make sure you go and follow Tim on Twitter @TimBenniks. And today we are talking about Uniform.dev. This is the tool we have been discussing. And I think from here, inprobably want to start at Contentful, right?
TIM: That's the beauty. You don't have to. And you know why?
JASON: Oh, interesting.
TIM: This is where it gets interesting. In the new hatless world, right, that we have nowadays, people, what they want is kind of what is my blueprint? How do I connect everything? And then the CMS vendors have jumped in and say, of course, we integrate with everything. So, they're kind of recreating this center of the universe thing where if you have Shopify, you put it into Contentful, and that comes through the Contentful API, right?
JASON: Gotcha.
TIM: But you don't really want that, because what if you want to change CMS or something or whatever? Suddenly, you have created this tight coupling. So, what we actually do is we don't start with CMS first. We start with the platform first where you put the blocks in that you like.
JASON: Okay.
TIM: And then you connect everything up and then you build your front end from there. That doesn't mean that Contentful is not important. Because all content editing actually happens there. Because we could never do better content editing than one of these CMSs. That's not the point. We just want to compose stuff on our interface, but we don't store any of that data. We only store a point or two, so you can query your front end yourself.
JASON: Okay.
TIM: So, what that allows, which is really cool, is that marketers can now say, okay, I need five products from, let's say, Commerce Tools, and I need Sanity to give me that component and Contentful to give me that component. They can literally just click around, put it in, done.
JASON: Gotcha.
TIM: The end point is the same. So, they don't have to ask a developer if they integrated it properly first. So, how about we start at build your own DXP, the button on the bottom‑left there. So, you might want to ‑‑ so, yeah, you can start here. So, basically, what you can see here ‑‑ by the way, if you scroll down, you will see a quote from Daniel, who is actually building Nuxt3. Which is kind of fun.
JASON: Yes, absolutely. So, we're going to start today with Contentful, right?
TIM: Exactly. We're going to keep it simple. We don't need a commerce engineer. So, you can just click "skip" below. Front end, let's do Nuxt and see how that goes. CDN, of course, we go Netlify. We don't have to do analytics now. We can talk about it a little later if you wanted to.
JASON: I'll skip it for now.
TIM: We don't really need Salesforce and that here. You could integrate with Salesforce so you could send a marketing email when somebody clicks, it personalizes, right? So, we have that integration.
JASON: Gotcha. And so you just brought up something that I think is really interesting here. Because what you ‑‑ what you're talking about, like, personalization then extends beyond just the website.
TIM: Exactly.
JASON: People get weird about, like, targeted marketing. I actually really like targeted marketing because I, like, if I'm being honest, I want things that are relevant to me. Like I don't need to see ads for, like, shoes that I would never wear, you know? Like get a sense of what my style is and send me things that I actually want to see, so that if I have to look at ads, they at least make sense for me. That doesn't mean I want you in, like, my bank records farming that kind of data. That's not what I'm talking about. I'm very much, you know, let's keep it reasonable here, but, like, can we make it relevant? Please make it relevant. Don't send me emails for services I already use. Don't send me emails for things I've opted out of. Only send me emails for things that are actually relevant. Hey, I've looked at all these products on your store. Are they on sale? Email me. It seems like that's what's possible here.
TIM: Well, the thing is, we don't actually want to add all that data. Because it's fully hatless, right, we don't really want to store that. So, we go with GitHub or Google, whatever you want. So, basically, what we do is we will get all that information and then we will actually just send it to your analytics provider of choice. And then you can query that one and then send your emails. We're just in between. Anyways. Here we go. Let's go through the guided setup. The first one.
JASON: Okay.
TIM: And then you can scroll down and go "continue."
JASON: All right.
TIM: You can see we have pre‑selected what you selected on the website before. So, if you had selected commerce tools and other things, those would also be pre‑selected. So, you'll get these integrations now set up for you so you don't have to click around. And that's just nice and easy.
JASON: Oh, wow, that is handy.
TIM: Or create a Contentful thing. So, now just ‑‑ I think you've already logged in before, right?
JASON: I have. I just got to ‑‑ somewhere in here is my actual login.
TIM: I didn't know you could have one password in your input fields like that. I don't have it for mine.
JASON: I think this is the newer, like, whatever the latest version of the plug‑in is or the browser extension.
TIM: Okay.
JASON: I'm also using Microsoft Edge, which I didn't think was a word I would ever say out loud, but it's great.
TIM: It actually works really well, doesn't it?
JASON: Yeah.
TIM: By the way, this integration that you now see, we have that with Content Stack and Sanity and others, and it works the same everywhere. We've kind of helped you out there so you don't have to fiddle with setup.
JASON: It's slick.
TIM: It can go as far as using Netlify templates to create GitHub stuff and environment variables, anything you want. So, that's pretty cool.
JASON: Yeah.
TIM: Okay. Let's see if that works out, to create a new space.
JASON: Should I have done ‑‑ should I have selected on old one?
TIM: No, let's try. It's all good. It depends on if you're allowed to actually create one. Yeah, so, you're not able to create a space.
JASON: I've reached my space limit. Okay.
TIM: Yeah. So, how about we just take one that you already had.
JASON: Okay. Wait, how do I ‑‑ let's retry.
TIM: You should be able to click out of this. Oh, there you go.
JASON: All right.
TIM: Can you ‑‑
JASON: Do I need to make it ‑‑
TIM: Did we just find the first bug? Let me just ‑‑ my boss is in the chat. Oh, wow. Can you maybe click ‑‑
JASON: I'm just gonna refresh.
TIM: It's fine. So, now, basically what happened is it created it for you. So, let's go to settings and integrations on the left. Yep. And so it did set up Contentful. It's just not configured just yet. So, let's just wrap anything. Because we can personalize whatever we want. It's all good.
JASON: Let's do that. Okay.
TIM: All good. Okay. So, what we've done now, you've now set up your ‑‑ something we call canvas, which is actually that interface that will help you to set things up, and you have a Contentful integration. Let's have a look at these integrations again. You can see what else we have there. So, if you scroll down, you're now in the free one. You can see, like, we even have I think Sitecore which are not really hatless systems. We can make them into one. Same with Salesforce, right, because lots of enterprise users are on these large systems with millions worth of investment there. We want to give them a safe path to modern stuff. So, you can integrate. So, how about let's have a look at this canvas tab on top.
JASON: Oh, canvas tab?
TIM: Yeah, yeah, sorry. Well, the thing is, we have not created a new space, right, so I don't really know what you have in your Contentful space.
JASON: Maybe we should log in and look at that.
TIM: Should we just have a look?
JASON: Yeah, let's poke in there, because I honestly have no idea what's in here. I picked the one that was for Learn With Jason, in the hopes I didn't do anything ridiculous.
TIM: It's all good. Let's have a look at the content model. You don't have much. You have some.
JASON: This is the one we did ‑‑ this is the one that we just did.
TIM: Oh, I've seen that. Wasn't that, like, last week?
JASON: It was last week with whitep4nth3r. Yeah.
TIM: Oh, cool.
JASON: So, good. This is great. It's 5 memories, right? So, each of these is ‑‑
You hackers. You dirty hackers.
JASON: YouTube embed and a description. So, not a ton here. But that's something that we can ‑‑
TIM: That's good. We can probably personalize this. Okay. Great. Okay. So, let's go into Uniform, and when you see on the left‑bottom, you see "component library," and here what we can do is we can actually configure a component we can work with.
JASON: Okay.
TIM: That links to your Contentful component. So, let's just create one and call them "memory."
JASON: Okay. How convenient that we just made one of these.
TIM: Yeah, that's very easy. So, then you can choose an icon, but that will be complicated. So, let's not do that just yet. Unless you know exactly what a memory icon is.
JASON: Okay. Let's just do, like, a cloud.
TIM: Yeah.
JASON: That's what a memory looks like.
TIM: Perfect. Okay, so, basically what you have here is anything you build inside this library is a component, right? And so it could be a page where you can put components into or it could be a composition component where you can put stuff into, right? So, you can add parameters, slots, and variations. And so for now, let's add a parameter, because what we want to do is we want to connect it to Contentful, right? So, when you select the type, you'll see that there is a Contentful integration there. And so it's a Contentful entry. And so it is a memory. So, it knows this already. So, let's call it "Contentful item," something like that.
JASON: Okay.
TIM: Because this is what we will be querying later on in our code.
JASON: Got it.
TIM: And so this is now actually one of the parameters of this component is now a memory from Contentful. If you wanted to, you can also add text parameters and other things here. If that ‑‑ if there was a need for that. I don't think for now that's true, but let's add one like this. And so now what we've created is basically a component that is linking to a Contentful memory.
JASON: Mm‑hmm.
TIM: But we also need to be able to place it somewhere, right? So, let's save this one on the right‑top. And then let's make another one that we actually make it ‑‑ let's call this ‑‑ let's call it a page.
JASON: Okay.
TIM: And then you can see that check box there that says "composition component," and so what this means, this can now hold other components.
JASON: Gotcha.
TIM: And so if you go to the second tab, you see "slots." We're going over this really quick, and I like that it seems to work, so that's cool. Basically, a slot is a thing ‑‑ like a placeholder that you can put stuff into. And wee see now that you can add a memory into the slot, but we also want personalization, probably, right?
JASON: I think so, yeah.
TIM: Let's call it "memories," so you can add multiple memories in one slot. Because we'll use this one in the code later on.
JASON: Okay. We probably want 1 to 3 so that we don't use them all.
TIM: Sure. Sure. It's up to you.
JASON: Why not?
TIM: You want to A‑B test as well, just for fun?
JASON: I mean ‑‑
TIM: Why not.
JASON: I love fun.
TIM: We use the same tag for it, so it's all good. Let's put it all in.
JASON: Okay.
TIM: So, now if you want, you see the title parameter thing there? Now that we've added a slot, I think ‑‑ oh, no, this didn't actually fill out. Because you can add a parameter with a title field there, and you'll see the title parameter because later on when ‑‑ just add a title field. Because in your composition, you might want to see what this thing is. So, it's kind of nice to add a title for people.
JASON: Okay.
TIM: And so ‑‑ yeah, okay, let's save ‑‑ see, now you can say this. Okay. Let's save again.
JASON: Okay.
TIM: And now go ‑‑ let's make a composition. You can just click, like, on the left, you have your compositions. Well, you don't have any, but let's add one here.
JASON: Okay. I'm going to select a page.
TIM: Of course it's going to be a page. So, it's going to be memories, I guess. Yeah, sure. And so now you can see, hey, there's a memory. Cool. Let's add it.
JASON: Okay.
TIM: And then you can see that it's actually on the right. It's actually a Contentful item, right? So, if you select the drop‑down, there's your memories.
JASON: Oh, look at that.
TIM: Isn't that nice.
JASON: Okay. All right. I'm following.
TIM: But you know what's fun here, is actually the flow now becomes ‑‑ if you ‑‑ we just set up what developers would do later on, right, for content editors and stuff.
JASON: Right.
TIM: If you didn't have to do that and you would just go in and click, hey, I want a memory, I want an arrow and something else, that just connects to different CMSs that you want or different ecommerces or whatever, that's pretty nice, right? So, now when you click "accept," there should be a memory connected. However, on the right‑top, you see it says "invalid," so let's see what that means. I think we might have not ‑‑ oh, so, we didn't add the page title. So, when you go back to the page on the top here, so, you have to add the title. Yeah, there you go. Yeah. Okay. So, now when you hit "save," this is the simplest, right? So, when you hit the little ‑‑ on the little arrow, on the "publish" button, you can see "view source." So, basically, this is your composition now in JSON.
JASON: Gotcha.
TIM: And as you look at that, you'll see there's actually no content of Contentful at all in here. There's just the ID that is the Contentful entry.
JASON: Right.
TIM: Because we really don't want to store the Contentful information here.
JASON: Mm‑hmm.
TIM: Because what happens when you un‑publish that component or you change it?
JASON: Right. Syncing is a huge nightmare. You don't want to duplicate content all over the place.
TIM: Exactly. That also means that if we did that that we have an opinion about something, that we format a certain way. We really don't want that. This really needs to be a platform that you just get stuff that you composed.
JASON: You know, I've noticed, Tim, that tends to be a hallmark of a really senior developer, is that all your opinions just become, I don't care, whatever you want to do.
TIM: Exactly. (Laughter) There is a drawback. There is a drawback, because, like, I thought I was quite good at what I did when I worked at Agency. I was quite senior. I was able to deal with CTOs and still write code. Then I got to this startup and I felt so stupid and so junior. These guys are ridiculous. So, it's great to be in a place where people are so good at what they do. However, what you then need to figure out is also, okay, so, what if somebody like me comes around that just wants to use this thing and not know every detail of your typescript types in the starter kit website, right?
JASON: Right.
TIM: So, we also need to simplify it to make sure that everybody can use it. And so it needs to be opinionless, but you also need to get out of the box SDKs to make it relatively simple to use.
JASON: Right.
TIM: So, what I think we should do now is go to the docs and start actually implementing this in a website. And so once we have something running, you can just go to docs.uniform.app. Or maybe click "help" on the right‑bottom, but this is also good. So, we want to go to the one on the right, "develop with Uniform." And so basically, now ‑‑ it's not a link. What's happening?
JASON: I wasn't focused on the window. Why isn't that ‑‑ what's going on?
TIM: I was a little worried there. So, what I actually want to do now is just build this Nuxt app, right?
JASON: Sure.
TIM: And get that composition that we just create on the page, just so we have it. Then we can take a couple of steps to actually do more and more to get from composition to the component data and then to map that data to how we want it. And then we add personalization, and then we can have a look. Because we have a bunch of developer tools, like, they are actually Chrome developer tool thing. Which really helps you with the personalization. So, we'll just take small steps.
JASON: Yeah, let's do it. All right. So, first and foremost, I need to set up a new Nuxt site.
TIM: Exactly.
JASON: And I'm gonna do that, like, npm init nuxt, or is there a way you prefer to do it?
TIM: Let's go to the docs and I'll show you. Skip the introduction and the glossary like developers always do. We'll go to the tutorials on the left. And then we'll just click on Nuxt. So, the beginning of this tutorial actually takes us through setting up that component that we just did, that memory. So, we really don't have to do that now. So, you see here it creates a component, and then this is one thing we should actually start doing now. We kind of need an API key.
JASON: Okay.
TIM: To be able to query what we've done. So, I'll just talk you through it, rather than having to read through it on stream. So, let's go to "settings," and there should be API keys.
JASON: Is this read only?
TIM: It could be right also, but it depends on how we set it up, but for now ‑‑ see, it actually ‑‑ our onboarding set one up for you. That's nice. Are you able to click on it and see it also?
JASON: Okay. So, I can ‑‑
TIM: Manage it ‑‑
JASON: Read drafts. We could manage. I'm not gonna check that box. Compositions, I want to be able to read.
TIM: Yeah, I would select ‑‑ yeah, I would also re‑draft. It's easier. You can just hit "save" and it works. This API, because we've realized ‑‑ imagine you have 25 pages all with different components and compositions and personalization, and you want to move from staging to production.
JASON: Mm‑hmm.
TIM: You don't want to reclick all those buttons to get that in, right? So, we have a CLI that will basically help you. So, let's only read everything. So, if you scroll down a little bit, it says "intent manifest" there. We also want to read that one.
JASON: Okay.
TIM: And tests ‑‑ no. So, I think we're good with this. And we can select your product ‑‑ project on top. I think we still have to select one. Oh. There's just one project. Okay.
JASON: Yeah, this is the one, right? This one?
TIM: Yeah, you're right.
JASON: Okay. So, I'm gonna save. And then how do I actually get the key?
TIM: So, Alex in the chat actually says you have to create a new one, because in the onboarding, we hit F‑5, and we only show you that key once.
JASON: Gotcha. Okay. So, this is going to be Nuxt app API key, and we're gonna put it on this one. We're going to ‑‑ it was read draft, read publish, we need it to read optimize, read manifest, and that was it?
TIM: By the way, these things work on SSR and build time, okay? Right, so, you need both of those. You need the key.
JASON: Okay.
TIM: And you need your project ID. And so, of course, this is a stream. We're showing a key now. So, people that are watching, we shall be removing this project later. Because there's keys on the screen now.
JASON: Yes. And these are read only. So, they won't do much more than just allow you to ‑‑
You hackers. You dirty hackers.
JASON: That's right, you hackers.
TIM: Exactly.
JASON: This won't let you do much more than read what we're gonna read. That's one of the reasons why we didn't choose anything other than read.
TIM: I like that. That's good. Because we are not here to show our CLI and stuff. Okay. Now you cannot see them anymore. Did you copy them?
JASON: I copied them, yeah.
TIM: Okay. Good. Awesome.
JASON: I'm gonna delete this one since it's gone. So, now we just have the one.
TIM: Yes. Let's go back to our ReadMe and see what else we need here. Okay, we did the project. There, it says "create Nuxt app."
JASON: So, I'll pull this up. We're going to NPX create Nuxt app and call this "personalization on the Jamstack."
TIM: Yeah, nice.
JASON: All right. Do you have a preference on JavaScript?
TIM: Let's go JavaScript for now. With Nuxt2, let's do JavaScript. For this, let's go very clean and not much ‑‑ we don't need any of this. Not for now anyways.
JASON: Okay.
TIM: If we want, we can add something, but we don't probably need it.
JASON: I'll leave Prettier in so it auto formats.
TIM: That's what I tend to do as well. We don't have to test for now. I would go, yeah, SSG, so, the first one.
JASON: Okay.
TIM: Then we go static Jamstack. There we go.
JASON: All right.
TIM: This is to your preference. I don't know what you want to use here. I don't think we need any of them.
JASON: I don't think I'm going to set up any of these because this isn't going to be a long‑lived project.
TIM: Exactly. I would go for none ‑‑ we want git maybe.
JASON: Yeah, we want git.
TIM: Unless you just want to use the Netlify CLI to deploy it.
JASON: I don't want to manually deploy from Netlify. That's a lot of work.
TIM: Agreed.
JASON: I'd rather it was all automatic.
TIM: Okay. Here we go.
JASON: Okay. So, let's peek into here. So, I'm gonna need to create these API keys.
TIM: Yeah, so, your environment variables. We have to install some packages and stuff.
JASON: Okay. Let me get in here. Personalization on the Jamstack. And I need to use, like, NVM ‑‑ let's use 16, I think.
TIM: I'm on 16 also, and that tends to work fine.
JASON: I need to add these ‑‑ so, what I'm gonna just to get us up and running, I'm going to GitHub repo. Let's see, what do we have in here? We've got a repo created. Let's git add everything. And we'll say git commit work in progress, setting up site with Uniform. And then I'm gonna GitHub repo create. This is the GitHub CLI, which is, like, my favorite tool in the CLI.
TIM: I haven't really used it much, to be honest.
JASON: Check this out.
TIM: This looks very nice.
JASON: It's so good. So, I do GitHub repo create. Create a public repo, hit enter, and now it's up there. So, I can git push. Origin main. And now that's live, so if anybody wants to see this now, you can go see this. This is just the default Nuxt app. That is live. And now what I can do is Netlify init.
TIM: That is decent.
JASON: And we're gonna create and configure a new site.
TIM: This is smooth.
JASON: I'll put it on my thing here. We'll call this personalization on the Jamstack. And it auto detects that it's Nuxt. So, we now have ‑‑
TIM: This is very smooth, Jason. I'm gonna have to look into that, to do this myself.
JASON: And so now the site is building. So, we've now deployed the site ‑‑ well, almost deployed the site. It it'll be live here in a second. But the really nice thing about this is now that we've done this, I don't need to create a.env anymore. We wanted to create uniform API key.
TIM: This is so smooth.
JASON: And then I'm gonna copy this.
TIM: I'm gonna be speaking to my colleagues in Slack to take note of this. Because when we demo, it's very nice to do it this way.
JASON: Did I do this wrong? Why didn't that work? I guess it did work.
TIM: It probably did.
JASON: Yeah, okay, it did work. The upgrade thing, like, caught me. So, and then we can go env: set. And we need ‑‑
TIM: Project ID.
JASON: Uniform project ID. Oops. Oh, no.
TIM: Oh, no. Now you have to edit it.
JASON: We'll just do that again.
TIM: Can you set it again?
JASON: Yeah, I can just override it.
TIM: Oh, nice. Very smooth.
JASON: Then if I do env: list, they're both there.
TIM: It should be there.
JASON: Set everything up. Now if I run Netlify dev, it'll fire up the site, but it fires up the site with those API keys in.
TIM: Yeah.
JASON: So, they're now there for deployment and they're also there for local development, and we don't have to manage, like, a .env and copy‑pasting that to the development settings. So, yeah, this is really nice. Okay. So, here's our site. It does nothing yet.
TIM: Nothing just yet. So, let's add some packages.
JASON: Okay. I'm ready.
TIM: Go back to the doc so you can copy‑paste them. It's easier.
JASON: Okay.
TIM: If you go up a little bit ‑‑ a bit up.
JASON: What did I miss?
TIM: Yeah, scroll up a little. There you go. So, Yarning or NPMing. Canvas is that kind of no‑code thing we looked at, drag and drop, and we have a Nuxt module that makes your life a lot easier. Then we need the CLI as well. This is the thing, right, when we're personalizing, basically what you do is we don't want to call an origin for personalization at all. So, what we do with the CLI, the CLI will actually go into your NPM scripts.
JASON: Okay.
TIM: So, on build, it will fetch stuff, put it in a file in your codebase. Then it will have all the things it doesn't need. It doesn't need to go to origin anymore. First, let's go to the docs and add the Canvas Nuxt module.
JASON: Do we have any modules in here yet?
TIM: It should have a default that's empty. If you scroll down.
JASON: So, I'm going to copy all of this.
TIM: I think that should be it exactly. So, those things should already work based on the environment variables that you set. And so this, number 3, is actually an important one, and a little bit to my personal annoyance, we need to poly fill fetch. Because we're doing this in SSR. And out of the box, it doesn't always work well. And so I know Nuxt poly fills fetch, but for some reason, it doesn't work. So, what we need to do is add a little plug‑in and actually import this fetch thing ‑‑ the isomorphic ‑‑
JASON: I installed isomorphic fetch not unfetch. My brain didn't even pick that part up. Okay. So, inside, the only thing we have to do is import ‑‑
TIM: You just have to import.
JASON: Okay. Fetch.server.js. Just create that.
JASON: So, I've got my plug‑ins. Fetch.server.js.
TIM: In Nuxt means that basically it runs on SSR mode, this plug‑in. But in your Nuxt convert, you have to tell it that this plug‑in exists.
JASON: Got it. So, back to the Nuxt config ‑‑
TIM: In that sense, Nuxt is a little bit easier to use, but also a bit more of a black box than Next. You can do so much more with modules and plug‑ins that you don't have in Next, but you don't really know how they work. Well, I do, because I have to, but it's kind of a black boxy kind of thing. We can talk about that later. I'm just looking at our chat. There's lots of people asking stuff I'm thinking or not. Oh, no. We're still good. Lots of people talking about my Dutch accent, it seems. (Laughter)
JASON: I see Alex raided with some friends. Welcome, everybody. Thank you for stopping by.
TIM: Nice.
JASON: Yeah, let's see. If you've got any questions and we didn't answer them, make sure you drop those in the chat, because apparently I broke the sound on my overlay. I don't know what's going on or why. Maybe I can just give it a refresh. And sometimes that works.
TIM: That tends to. Oh, you're using slobs, right? What's the name?
JASON: Yeah. This is StreamLabs OBS.
Oh, so, we would need to add code.
JASON: There it is. It's back. This is StreamLabs. I think sometimes StreamLabs has its moments. If you turn it off and on again, it works. All the overlays for my stream, too, are just a website.
TIM: Yeah, so cool.
JASON: All the content, everything pulls this ‑‑ this down at the bottom here, this is all pulling from, like, a GraphQL subscription API. It pulls from Sanity's API. All my sponsors are Dynamic. I just check boxes and logos show up. It's really nice.
TIM: Okay. We'll talk about that another time. Because I'm really into that stuff. (Laughter) I once did it with ‑‑ I played video games on Twitch and my overlays had web sockets that would look at the stats of the game and update my overlays. It's so fun. Anyway, let's replace our NPM scripts with this. I'm thinking you're looking at the docs that are a little outdated. Because we might need to install ‑‑ do you see on line 6, now it says "run S?"
JASON: Yes.
TIM: So, series. We need to install a package called NPM run all. NPM‑run‑all. Yes. I think it's called this and that should ‑‑
JASON: I think you're right. I remember that one. Oh, wait, I screwed up.
TIM: Uh‑oh.
JASON: Got to copy this.
TIM: Oh, you didn't save ‑‑
JASON: I didn't save before I installed the thing. So, we're gonna open it again. And now ‑‑
TIM: I think we've all been here.
JASON: It's actually here.
TIM: Okay. Cool. So, this should work now. We did actually do a pull request to our Canary branch with all the fixes for the doc, but I don't think it went out yet. Living on the edge, man. This is the startup life.
JASON: Okay. What I can do with Netlify dev then is also have it run NPM run dev.
TIM: Exactly. Because if you look at our package JSON now, you'll see that we're doing certain things a bit differently. You see there is a bunch of stuff going on here. So, why is that not working? Okay. So, basically we haven't published what we worked on, it seems.
JASON: Oh, okay.
TIM: Let's have a look. So, let's go back to our project.
JASON: Nope. It was ‑‑
TIM: I think it was Canvas.
JASON: Canvas. Oh, right. That's where it is.
TIM: And then you go to your composition. Oh, it is published. Okay. So, in that case, we need to do something else, because when you look at that error ‑‑ just click "publish" to be safe. So, when you look at that error, it's actually finding an issue with going to the manifest file. Which means, okay, so, we didn't talk about this much, but the manifest file is basically a JSON file.
JASON: Mm‑hmm.
TIM: That is fetched on build and has the configuration for your personalization in it, but we didn't add any personalization, so we didn't publish anything, so, there is no endpoint for your project ID. So, we go to personalization.
Ahem.
TIM: I love these overlays, man. For now, we don't have any. Just click "publish," and you should have an endpoint.
JASON: Published. Let's try it again.
TIM: All right. Let's try this again. So, to use the magic of ‑‑ there it goes. It works. To use the magic of the Jamstack, we are actually in our build fetching stuff, putting it in your bundle to really use the magic. So, why is this not working? But I don't think we finished up everything just yet in that walkthrough. Let's go through and see.
JASON: Oh, yeah, we just skipped a bunch of stuff. (Laughter)
TIM: As we do. I certainly always do that. Okay. Let's have a look. What we need to do is ‑‑ go to your index.vue, which basically shows you the default thing from Nuxt, right? What we can do is remove that whole template thing and put in a little diff with a header ‑‑ we don't need this stuff for now. We'll use it later, of course, but something. Exactly.
JASON: Okay. So, we'll do one of these.
TIM: It's probably not working now, because it could be an environment variable thing or we just have to finish the walk‑through.
JASON: Okay. So, we need to export. Default. I already did the export default.
TIM: The interesting thing is we're not really doing anything now. So, I'm kind of interested ‑‑
JASON: I wonder if ‑‑
TIM: Did we ‑‑ something with environment variables might have gone wrong, to be honest.
JASON: Yeah, let's look at that.
TIM: It looks like the API key is not added through the variables.
JASON: Let's open the developer tools. And let's look at the console. And let's try again.
TIM: I think it's ‑‑ this is an SSR error, so you most likely will see it in your console from your terminal.
JASON: Okay.
TIM: No, I think you have to open your terminal.
JASON: Here's the terminal.
TIM: Can we scroll up a bit? Because it seems to compile.
JASON: Mm‑hmm.
TIM: I think for some reason the environment variables are not working. That's very interesting.
JASON: Okay. So, we've got ‑‑
TIM: You see ‑‑ it did know your project ID.
JASON: Yep. Local host 500. Network.
TIM: Yeah, so it seems your API key is not working. Let's have a look at the ‑‑
JASON: What is trying to load that would cause this to happen?
TIM: Well, what happened is we installed the Nuxt module, and that Nuxt module, you gave it an API key. On server‑side render, that Nuxt did a bunch of stuff already. So, let's have a look at why that might not work. Did you ‑‑ oh, did we remove the wrong API key from the dashboard?
JASON: I certainly could have. Let's try again.
TIM: It would be something I would do, for sure.
JASON: API keys. API keys have been moved.
TIM: Yeah, they're team level now, sorry.
JASON: Okay.
TIM: So, yeah, we can actually not see it now.
JASON: That was the one that I created, but let's just create a new one. Let's just try again.
TIM: Yeah, just be safe.
JASON: Personalization on the Jamstack.
TIM: Yeah. So we only read things.
JASON: Read.
TIM: And we need to ‑‑ yep.
JASON: Read.
TIM: And we need to read the manifest, and this is all.
JASON: That's all. Okay. Creating the API key.
TIM: Okay. And now let's set the environment variable again.
JASON: Okay. So, this time, I'm just gonna do it right through here to make sure that we've got the right one. I'm going to build and deploy. We're gonna go to environment and we've got ‑‑ did it only set the ‑‑
TIM: Oh, it didn't ‑‑ that's weird.
JASON: Interesting. Betrayed. Betrayed by probably when I double set that, it broke it on me.
TIM: For some reason, locally you have your project ID, but not here.
JASON: Yeah, something went wonky on me. Let's get that project ID out.
TIM: Yeah, you can just find it here. There it is. Yep.
JASON: Go back here. Set it. Save it. Okay. I refresh the page.
TIM: But now ‑‑
JASON: Build error.
TIM: You should actually ‑‑ should you get this locally now? Do you have to fetch them?
JASON: Should get it locally.
TIM: That is nice. This is such a nice way of using this.
JASON: Oh, so, I need to publish the manifest again?
TIM: I'm thinking. I love how we're just trying to figure this out. But we'll get there. It's all good. Security is very important, right?
JASON: There it goes. Things are happening.
TIM: Okay.
JASON: It is ‑‑
TIM: Oh, it's still compiling. Okay.
JASON: Still compiling. We've got an error. What's the error this time?
TIM: So it's again a 500. Why?
JASON: Yeah.
TIM: For some reason, it also doesn't really show you on the page itself.
JASON: Mm‑hmm.
TIM: Mm‑hmm. I'm not gonna be tempted to share my screen and show you properly. We're gonna fix this.
JASON: So, yeah, what's going on here?
TIM: So, it seems to be ‑‑
JASON: Something is really weird here.
TIM: Well, and it's still loading as well.
JASON: Yeah.
TIM: Can we try, just for fun, to copy‑paste that API key hard in our Nuxt config?
JASON: Yep. Let's do it. Maybe.
TIM: You just have to edit it, I think.
JASON: All right. So, in our Nuxt API config, we are going to ‑‑
TIM: Oh, and also, we might want to save ‑‑
JASON: Oh, wait, I know why.
TIM: Oh, no, this is still the ‑‑ holy crap ‑‑ I'm sorry to say that on the stream. We have updated our docs, but not released it. Gotcha. I apologize for that.
JASON: This is gonna be great. It's gonna work this time. We're going to try this one more time. You're doing it, Peter. You're doing it.
TIM: I'm flying! (Laughter) I think we're the same age, right? (Laughter) Okay. But this takes super long. Oh, wait, you have to save your Nuxt file.
JASON: Do I need to kill this thing? I'm gonna just try this one more time, because I think we also just overloaded this tab.
TIM: Yeah, probably. And I think your index view is ‑‑ oh, yes.
JASON: There we go.
TIM: All right. Okay. Copy from ‑‑ I'm writing my notes. Anyways, we did it.
JASON: We did it.
TIM: We still have to save this file, though.
JASON: Oh, wait, I didn't finish ‑‑ I didn't finish this thing that I was writing. So, let's just undo that. Back to ‑‑ there we go.
TIM: Now you can see it again. That function you were just writing is basically get static props from Next, but we're doing the Nuxt variant now. So, you can literally just either type it or copy‑paste it in the interest of time.
JASON: Okay. Async data ‑‑ you're right. In the interest of time, we should probably keep trekking here.
TIM: So, what this thing gives you is the Nuxt context. The async data gets all the knowledge Nuxt has of SSR and client. And our Nuxt module has basically injected itself, provided some data, like a React context.
JASON: Mm‑hmm.
TIM: It injected the Uniform Canvas Nuxt context. So, what we are doing here ‑‑ sorry.
JASON: Yeah.
TIM: Oh, okay. Sorry. This, again, is from the old doc, so, we need to remove that .client there.
JASON: Okay.
TIM: And what we also need to do is currently it's querying a slug that is called slash. In our interface, if we give it a slug.
JASON: I don't remember doing any slugs.
TIM: Let's go to the memories one. And here you say "add slug" on the left‑top. And just call it "/memories," I guess.
JASON: Okay.
TIM: And now we save this. And now just click. So, what we should do now. So, it's querying the composition, and we have, like, this little helper function that is called get composition by slug. And so what we can do is copy that composition that it's returning and showing that in our template. So, what you can do is literally just type a pre tag. And then in double curlies I think you call these things. No, you don't have to do that. It does it for you in Vue.
JASON: Oh, because of course it does.
TIM: In Nuxt, in Next, in React it's JavaScript. Here, it's black magic. And now it should show you.
JASON: Still loading.
TIM: It's taking too long for me. Did we save it?
JASON: I think I did. Let me see. Didn't like ‑‑
TIM: Ooh.
JASON: Blocked by Coors.
TIM: But that should work because it's actually an SSR. Can you do a hard reload?
JASON: There it is.
TIM: Yes! What you might have seen a hot module reload where Nuxt actually runs SSR code in the client. I thought that was fixed. I'll have a look at that. Anyways, what you see now, this is actually your composition.
JASON: Yes.
TIM: But it's nothing just yet, right? It's just a Contentful item inside a memory slot.
JASON: Mm‑hmm.
TIM: And you added some parameters, like a title. So, keep in mind that this can be 20 components. With lots of different things that have sub‑slots. Like on my website, I have a two‑column component with a slot on each side, and inside there, I could do a rich text and a single image and then personalize that thing and that can have a slot again. So, you can make a whole cool thing there. But, of course, we're keeping it simple now. So, what we need to do is use our enhancer API to actually fetch that data from Contentful and then map that to something that we want, and then render a component with that. So, let's go to the ‑‑ back to the docs. So it's easier to copy‑paste.
JASON: Yes. I'm pulling over ‑‑ Alex put in a ‑‑ the Canary version of these so we can ‑‑
TIM: Ooh, very nice. At least we know it will work.
JASON: All right. So, back down to where we were.
TIM: Okay. We just did this. We did the pre with the composition. Okay. So, now what we want to do is use our native component that we added called "composition," right? Let's just have a look at what that is and copy that in.
JASON: So, we write it in as composition. So, composition is a component.
TIM: It's a Vue component or a React component. In this case, it's a Vue component, and we're giving it a prop of our composition.
JASON: Okay.
TIM: And so what this thing does now, do you know the concept of slots in Vue?
JASON: Mm‑hmm.
TIM: Yeah, so, basically, it creates a default slot for you with a bunch of data. And so it gives back the parameters that you gave to your composition, but it also gives back the slots plus the components that are inside the slots. And so ‑‑
JASON: Got it.
TIM: ‑‑ for now, what we can do is maybe just copy that line that says "template‑default = parameters." Let's put that in. Now we have a default slot in Vue. Look at the parameters thing to see what you're getting. So, we'll just slowly build it out, and at one point, personalization comes in and you'll see how easy that will work.
JASON: Okay. So, I'm gonna refresh the page here.
TIM: Ah, and we found something that's important. That should have been in the docs. Because when we now look at the Canary docs, you will see that we should have added our personalization package. Because the moment you use the composition component, it does auto magic personalization stuff for you. It should say somewhere you have to install that. That is it. Optimize. Yes.
JASON: Okay. What did I just run? Oh, boy.
TIM: Oh, we're doing the ‑‑
JASON: It's okay.
TIM: That's all good.
JASON: Don't you do that. All right. Let's just do the one part. All right. Get rid of you and just try that with a ‑‑ one thing.
TIM: It copied some Yarn stuff there.
JASON: I didn't realize this was on the next line, so I copied too much stuff. So, back down we go.
TIM: To be able to actually do the personalization bit. Let's see. We might need to add a little bit of configuration to that optimized package. Oh, yeah, there it is. If you scroll down a little bit.
JASON: Just need to put this into ‑‑ here's our Nuxt config. Let's put it into plug‑ins.
TIM: This is also a module.
JASON: Also a module?
TIM: Sorry, yeah, that's the next thing. Yeah, so, just put it below here.
JASON: Okay.
TIM: Is that on the right? Yes. So, now we can personalize, but to be able to personalize, you need to track somebody, right?
JASON: Okay.
TIM: So, what we do is we have a tracker that is instantiated on the page, also in SSR, and basically it follows what a user does. We need to create this ‑‑ we chose ‑‑ if you put a folder called tracker.js. You have to make it. That's a bit of a React‑y kind of thing. Optimize. Oh, you need an E after the Z.
JASON: Oh, whoops. Yeah. Uniform optimized tracker.js.
TIM: So, this is the simplest tracker that you can give somebody to track what they do, so now we're not saving anything anywhere, which is very safe. So, this is not ‑‑ this is completely GDBR compliant and everything. So, what you can do here is add cookies and do other things. Because if you add cookies for this tracker, you can also use your Netlify edge handlers to read the cookie, no personalization, and render something for you on the fly and put it back into the Jamstack side. That's for way, way later. We're not there yet. We may not actually get there just yet. Anyways, this should now work. So, maybe what we can do is another NPM run dev and see if everything works. I'm sorry, Netlify dev. It fetches the variables.
JASON: It still doesn't like this tracker initializes.
TIM: We might need to give it a hard refresh.
JASON: There it goes.
TIM: Yeah, yeah, what it did, a hot module reload without knowing about that new plug‑in that we just added.
JASON: I was still dumping this, so I'm gonna take that out.
TIM: We also don't need the chat thing, I guess. Okay. So, what you just now did is you dumped only the parameters, right?
JASON: Why did that double up? Oh, there it goes.
TIM: I don't know. I have to be honest. Lately, the Nuxt2, the latest build, has a little bit of these quirks. I haven't seen ‑‑ like the stuff you just saw with the hot module reload, it's a little weird. It's always complicated.
JASON: I'm glad somebody else thinks about that stuff, man. It's so hard.
TIM: Oh, yes, I couldn't agree more. I'm quite happy when that comes out when it's completely different. Anyways, that's another stream, I guess. So, what we've done now, you get this scope slot, right? You can do a bunch of things. You can also actually get the slots that you get back. So, you'll get back Uniform slots, not Vue slots, of course. So, let's have a look at ‑‑ so, here, what they do in in the ‑‑ we don't have to follow this, but basically you would ‑‑ you are able to get these parameters to a component and just render that component, right? But we want to actually do a little bit more. We want to get the slots back, and inside the slots are components that can then get rendered for us. So, let's see ‑‑ yes, here. So, basically, what we want to do is grab that slot content component.
JASON: Okay.
TIM: And we're gonna look for ‑‑ well, we only have one slot. So, most likely we don't need that key. We can just call it "memories," probably.
JASON: This is the slot, though, right? We called the slot "memories?"
TIM: Exactly. And so I'm thinking now it will try to create you a memories component, but, of course, we don't have that just yet. But let's see. Let's see what the docs say. It might now give you an error because it just doesn't now have that component.
JASON: Sure, yeah. Okay. So ‑‑
TIM: Let's scroll down a bit. Okay. So, we can go ‑‑
JASON: Down to the slots. Here we go. So, the slot content.
TIM: Yeah. Okay. So, this is what we have. Okay. So, let's go on. Okay. Like, basically now it wants you to create a promo.Vue. In our case, we're gonna make a memory.Vue inside of components.
JASON: Okay. So, let's open up here. We've got components. We're gonna make memory.Vue.
TIM: Yes, exactly. The component time we had was called "memory," right? So, this will probably do it. In Vue, you need to give it some props. Just copy this guy and it should probably do it already. It always gives you a Uniform data prop. Whatever content comes from it. So, let's save it. Nuxt should ‑‑ because it auto resolves for you. You don't need to load them in like in Next.
JASON: Got it. That's nice.
TIM: That's pretty easy. So, that just worked. So, how about we add a little bit of ‑‑ well, we need some code, because right now it's only that IT, right? We actually need to be able to query something to make it look good. So, let's go back to the docs. And then it should probably ‑‑ let's see if it tells you more on that. Otherwise, we can click on the ‑‑ we don't need all of these for now. Nope. This is all with sub‑slots. We don't need to do that for now. We also don't need to add variations for now.
JASON: Do I need enhancers.
TIM: Yes, because what we actually want to do ‑‑ you have your ID, and what you want to do is enhance your component data by, first of all ‑‑ well, this looks like a whole bunch of code, but we have out of the box stuff that I'll show you in a sec. And what you can do, this will actually query your Contentful ID for you, put back all the data, and then these enhancers can also help you to map your content to whatever data that you want. If you want to answer a dad joke to every property, you can, right? So, these enhancers, they can live in your code on SSR, right, for when you actually build.
JASON: Mm‑hmm.
TIM: Or what you can do is have them live on a serverless function. And then suddenly it becomes an API endpoint. It enhances the data for you and your codebase doesn't need to know where everything comes from. You just query that one endpoint. It will do all the data mapping you like from every CMS and give it back to you. If you go back to the docs, if you make it bigger, you'll probably see a Contentful link on the sidebar. We don't need to read all of this. We just go to the code, as we do as developers. So, basically what we need to create is a Contentful enhancer.
JASON: Okay.
TIM: And this one uses get static props from React, right? But what we need is we need to make an enhancers folder. Let's see. We might not need to make an enhancers folder. What we can probably do is you can grab from basically the second const from there. Just copy‑paste it from there.
JASON: This one?
TIM: No, we don't need this because this is what our Nuxt module does. From here. We also need that enhance thing. So, we need to do this in our page.
JASON: In the page? So, here?
TIM: In the index Vue, we have the composition now, but we need to do something with that composition. So, this needs to be actually in the same function that we just wrote.
JASON: Okay. So, I'm gonna take this and we're gonna put it here.
TIM: Just there. Exactly. So, we're gonna enrich or enhance that composition. And actually, what I just realized ‑‑ you see that line 19, Contentful enhancer? It creates a Contentful enhancer. We do need that client property that I told you not to copy. Sorry.
JASON: Okay. So, we need this one.
TIM: So, this actually creates you your ‑‑ this is the client to query Contentful with.
JASON: So, does Nuxt insert all of this or do I need to go get it?
TIM: We actually need to go get it and put it in.
JASON: Okay.
TIM: So, we're going to be showing ‑‑ you can add this to your environment variables, right?
JASON: In mm‑hmm. Okay. So, environment access token. All right. So, let's get over here. And we know that this is the space.
TIM: Exactly. That's an easy one. And then environment's going to be master. So, that's easy as well.
JASON: And then the access token, that's read only.
TIM: I hope it is. It probably is.
JASON: If I remember correctly, it should be. So, let's create a new one. And this one will be ‑‑ this will be Uniform, and it's going to be the delivery API, which is the one we want. So, let's copy that. And since we're already in here ‑‑
TIM: That should be enough. Yeah, just add it.
JASON: Yeah, you know what? You're right. Let's drop it in. Eh, screw it. We should make all ‑‑ these should all be environment variables.
TIM: Of course.
JASON: We are very short on time and I want to get to the personalization part.
TIM: Exactly. I just want to show you that. So, basically what this does ‑‑
JASON: Where does this come from?
TIM: This comes from actually our package. You look back at the doc and it creates a bunch of imports. Need to add these to and install them in our project.
JASON: Okay.
TIM: So, let's go up before the export. Yeah.
JASON: Okay.
TIM: And so most likely, Uniform Canvas Contentful we have to install, and Contentful itself. That's it.
JASON: Whoops. All right. And thank you for the raid, CodeWithAsan. Thank you, everybody. Hope you're having a good day. Run. Save.
TIM: Yeah, we're not using that one, so you can just remove it. Our Nuxt module makes you the canvas client for you. So, let's see if something else changes.
JASON: Ayo!
TIM: There it is. All right. So, in the interest of time, let's not look into what it does. The simplest thing is it grabs the ID, it queries it for you, and returns you all the data.
JASON: Yeah.
TIM: And you can do anything you want with that. You can map that data for anything. But for now, we don't need that. So, let's keep this simple. What we could do is add some simple HTML to go over these fields. So, we show something, so then we can look at the personalization business.
JASON: Okay. So we change this one to parameters. It's ‑‑ I want to go parameters.title.
TIM: Yeah, .value. .title.value, I think. Let's see.
JASON: There's one. Cool. And then if we get into the memory.
TIM: Exactly ‑‑
JASON: Then we can do something simple.
TIM: Yeah, uniformData.‑‑ Contentful item that value ‑‑
TIM: I think it's parameters.Contentfulitem. You can have other parameters as well. So, it's parameters here.
JASON: .parameters. Contentful.
TIM: Contentful item.
JASON: Item.‑‑
TIM: And then fields I think.
JASON: If I can spell right.
TIM: This is pretty long, right? With your own enhancer, if you wrote one, you can make this very short, because this is basically what they give you back from Contentful. So, let's have a look if this actually shows us something.
JASON: There we go.
TIM: Yeah.
JASON: So, then we can do some basic stuff. So, out here somewhere, I have the embed code.
TIM: You have a YouTube embed, right?
JASON: Yeah. Let me just grab that real quick. So, I'll bring that back in, and we can drop that in. And we need to replace this part with ‑‑
TIM: I think it's ‑‑ isn't it the full URL that you're getting from the model or not?
JASON: Ah, no, this one is just the ID. Oh, it is the whole URL, you're right. You're absolutely right. So, that's just me thinking I didn't ‑‑ mm‑hmm.
TIM: So, put that one in. But this is not gonna work, because in Vue what you want to do, if it's a property like this like HTML, you want to put a colon before SRC. Before SRC.
JASON: Oh, and then just drop this out.
TIM: Exactly. So, it's close to JSX. It's just not completely the same. But there you go.
JASON: Okay. I'm gonna drop this out. We have now created content.
TIM: Exactly.
JASON: Aha!
TIM: And so ‑‑ this ‑‑ the whole point of this is that potentially you can do this with multiple different hatless sources and ecommerces and just build your page like this, if you code it the way you want.
JASON: Right.
TIM: So, let's go back to our Uniform dashboard and let's add some personalization now. So, before we personalize, we also ‑‑ like right now, it won't work because we don't know what we're personalizing against.
JASON: Okay.
TIM: Think about what is the intent of a user? You cannot delete it because it needs one at least.
JASON: Oh, got it.
TIM: Yeah, we'll get there in a sec. So, when you go back to compositions. So, go back one. Yeah, let's not save it. It's all good. Click on "personalization." And inside personalization, we can start adding some intent. So, what would ‑‑ how would you like to personalize a memory?
JASON: Let's say if somebody is a JavaScript‑focused developer, we're going to show them Rich Harris' talk. And if they are animation‑focused developer, we will show them Cassie Evans' talk.
TIM: Perfect. Okay. So, let's call the first intent JavaScript.
JASON: Okay.
TIM: Yep. That's all we need. And so what we need to add is a signal against JavaScript. Because we need to know what happens here. So, let's do "behavior."
JASON: Okay.
TIM: Oh, you know what? Maybe we can do "behavior," so you go to a page and our tracker tracks that. Or what we can do is a query string. That's easier to demo now. Click cancel ‑‑
JASON: Yeah, let's do that because we are very much out of time here.
TIM: Okay. Query string. Just call this "query string." Or "QS" or something. It's just the name of the thing. So, what is the query string parameter actually? Language or ‑‑ no.
JASON: Preference. And we can say JS for this one.
TIM: Yeah, exactly. And keep everything the same. It's all good. Let's not go in there now. Okay. Now go back. Make another intent. And then call it ‑‑ what did you want to do? Animation?
JASON: Same thing. Add a signal of query string. This one will be QS.
TIM: Preference.
JASON: Preference. And we'll do animation.
TIM: Everything the same. You can do so many things here, but let's not. Click "publish" on the right‑top. Normally there is a web hook going to Netlify that rebuilds your site, but we haven't set that up. Now what you need to do is restart your local thing, but we don't have to do that yet.
JASON: Okay.
TIM: Just go to memories. And then click on "memory," and now you can click "personalize this."
JASON: Okay.
TIM: So, the first memory is for animation.
JASON: Okay. Now I'm gonna change this.
TIM: You might want to unlink this one or edit it.
JASON: Oops, didn't realize that's what that did. Okay. Let's un‑link. We're gonna choose a new one. This is gonna be GreenSock with Cassie. Create another one.
TIM: You should create it inside personalizations. Now you're creating another one.
JASON: Oh, wait. I gotcha.
TIM: We need another that is now a memory that is for JavaScript. And we select one.
JASON: Okay. Cool. Then we can have one that always works, which will just be a memory.
TIM: Exactly.
JASON: And that one we'll make ‑‑
TIM: Cool. So, what you need to do is click on "personalization," because it's asking you to add something to it. So, when you click on it, they need an analytics tracking name. Just call it "preference" or "language." For match criteria, click the first one. We don't go into that now, but the basics are there. So, let's publish this thing. So, we have two ways to easily see this. Or you install our Chrome plug‑in that I can send you a link to right now. Or we can try that query string.
JASON: Yeah, let's try the query string.
TIM: That should be easier.
JASON: And so what we've done here is ‑‑
TIM: We might need to hot refresh this. Yeah, you might want to just refresh. Okay. So, now there is no personalization, right? So, it's showing Salma.
JASON: Oh, wait, I did something wrong. Missing required prop: "SlotName," what did I do?
TIM: Let's go back to the code. I didn't know that it would do that. All good. Go back to index. In your slot content, you want to do slot‑name = "memories." Now it knows where to look and probably shows you the same without the warning. Okay, cool. So, let's add this query string.
JASON: All right. So, I'm gonna add a query string of preference, and we'll say JavaScript.
TIM: And there it is.
JASON: Okay. And then we'll add animation. Uh‑oh. I did something wrong.
TIM: Hey, that's interesting. Can you refresh your page?
JASON: Yeah, it showed for a second and then it bounced out.
TIM: Ooh, interesting. I hope we found a bug so I can fix this when we go proper launch it. That's very interesting. Okay. We have a little bit of time. I'm gonna send you a link and then we can easily debug it.
JASON: Okay.
TIM: Should I send it to you, like ‑‑
JASON: Drop it in the Twitch chat. That's probably the easiest way for me to get it.
TIM: Oh, actually, Alex is right. This is interesting. So, basically what happened is that they both have the same score. And then it doesn't know which one to choose.
JASON: Oh, I have both scores.
TIM: Yeah.
JASON: Is that in my application memory somewhere?
TIM: You can look in your console, but you can also just ‑‑ if you install that plug‑in, it's much easier to see everything.
JASON: Okay. Let's install the plug‑in.
TIM: Yeah, yeah.
JASON: Is there a way for me to do ‑‑ allow extensions from other stores?
TIM: Oh, yeah, of course, you're in Microsoft. It should work.
JASON: It should work. Okay. So, now I have this.
TIM: Maybe you might want to refresh. You see the icon just appeared. Yeah, okay, click on the icon. It sees you have animation and JavaScript, both a score of 50. So, let's go to "what if" in the middle.
JASON: I just reset it to make sure it does what we expect right out of the gate.
TIM: But for some reason, it's not showing the animation ‑‑
JASON: Doesn't like Cassie's for some reason. Let's go to "what if." Let's put animation.
TIM: Let's set the score.
JASON: There it goes.
TIM: Very interesting. I do know that with Nuxt, sometimes when there is, like, a React or ‑‑ sorry, a Vue router change, it might not always find it, but it's really strange to me.
JASON: I'm assuming that's adjust something that got stuck in my browser. But this is good.
TIM: Who knows.
JASON: So, this is also really nice. This "what if."
TIM: It's very easy.
JASON: Go say here is JavaScript. I'm gonna set my animation to 5. Come back here and see my intents.
TIM: Exactly. And we amazingly built this because you might get 25 different components all the way to a little bit of score. And then how will your QA people figure out what shows when?
JASON: Right.
TIM: You might want to "what if" that, right? What we can do here, let's say click on "checkout" because you want to buy a memory. You don't want to show everything anymore. Then you want to show, hey, you bought something or whatever. You want to decrease score somewhere and upgrade it somewhere else. You can also say, if I have a certain score accumulated, only then I show a different piece of content. There are so many different things you can do. So, you really want this little tool to help you. This actually works in the Jamstack. So, it looks at that score based on the actions that you did and then it will show. Let's have a look what Alex is saying in the chat.
JASON: I'm grabbing that learning now. This is using Netlify edge handlers, which is exciting.
TIM: Oh, really? There we go. He says even remove JavaScript. That's gonna be fun.
JASON: Oh, disable JavaScript. Yeah, let's do it.
TIM: Go edge handlers ‑‑ now we go full‑on.
JASON: Okay. I'm disabling it.
TIM: Last four minutes. This is where it's at.
JASON: So, JavaScript is disabled. Right? So, Node.js is working. It still does that CFP ‑‑
TIM: Of course, there's no JavaScript. You can click on "for developers." It should still remember it because of the handlers.
JASON: Application.
TIM: Click on the "for developers" link. It should be now increasing scores against developers. Now when you go back home, what do you see? Probably for papers was stronger. I think call for papers is really strong. Let me ask Alex. It might just be way strong.
JASON: Okay.
TIM: Can you turn on JavaScript just to check?
JASON: Yeah, let's do it. Go in here. Behavioral signals require JS. Okay.
TIM: Oh, of course.
JASON: Come on. Be ‑‑ no, do it.
TIM: Almost there.
JASON: Oh, my gosh. Come on. How did they spend the last five minutes? Well ‑‑ okay, so here's ‑‑
TIM: Check box.
JASON: Got JavaScript back.
TIM: Refresh.
JASON: I'm going to "what if" this and take that CFPUTN back to 0. It says welcome to UniformConf. Let's me turn off JavaScript again so we can see this is still doing what we expect.
TIM: I want to see this working, exactly.
JASON: So, here is disable JavaScript. It's off. Okay. Working as expected. Now let's hit this UTM.
TIM: It's two down. There it is.
JASON: Now JavaScript is still disabled. Like we don't have JavaScript running.
TIM: Now when you go to the homepage, it should probably also still show you that UTM campaign because you have scored for that, and the edge handler is actually injecting it for you. So, if you just go to the homepage and remove that query string, you still see it.
JASON: Okay. And so we can see that, like, the CFP is here, but I hit "dev," so it updated my dev score.
TIM: Exactly.
JASON: And that's because we turned JavaScript back on. And so these signals actually fire. This is really cool. I really like this because it, you know, it gives us, first of all, what Alex is saying in the chat about, like, if you don't have JavaScript enabled, you can still do personalization based on query strings, and this doesn't require you to, like, fully build out completely custom pages, like, new landing page for everything that you do.
TIM: Exactly.
JASON: Which, like, you know, we even do that at Netlify. We've got specific landing pages for all sorts of things. That's a common ‑‑ a common challenge for us, is we have to make those happen.
TIM: It is. And you know what? You should actually ‑‑ well, this is not a product page here, but maybe you want to look into how we do this Canvas build thing for your landing pages. Because we kind of built it for that.
JASON: Yeah.
TIM: This is very easy now for ‑‑ let's say you need to build a page without touching much code, right? You need to build 25 of them. Relatively similar, but with different content. Just go in there, drag around, add some personalization, good to go. What you can do is you know me ‑‑ because I have a Netlify account, right? So, you know something about me. Let's say there's a cookie or you have something you can query from a serverless function. On page load, you can push that into our tracker and directly personalize because you know me already. And that also works in the front end on Jamstack landing page.
JASON: Nice. That's really nice.
TIM: So, what you've seen today ‑‑ and I know we're out of time. I pushed a little bit to try to get you to build that, like, platform thing. Because that really helps you out to kind of ‑‑ what this means is that personalization now becomes this contextual thing again. Which is just a thing that's at ‑‑ it's added in and it just works.
JASON: Mm‑hmm.
TIM: Rather than you have to do a whole full project to get it to run. And so what actually generally what I say to developers is, like, if you wanted to do personalization, and all these bigger brands want to do it, right? Probably Netlify wants to do it. Why not? You know me. You know I went to Jamstack Conf. Show me a different homepage, right on Netlify homepage.
JASON: Absolutely.
TIM: It's simple. With our tools, you can actually do this in your MVP build, rather than in version 16 down the line because it's so complicated to set it up, right? Oh, you've got a pretty big raid here.
JASON: Big raid right at end. We are actually going to be handing you off because we just ran out of time. So, yeah, Tim, everything you're saying is amazing. Unfortunately, I got to cut you off because we got to wrap this thing up.
TIM: I know, man. I'm sorry.
JASON: So, for everybody who is watching, if you want to learn more, I just dropped the Uniform dev link in the chat. Let me drop that again. We've also got Tim here if you've got any pointed questions for Tim. Go and hit him with those. And one more shout‑out to the sponsors. We've had Jordan from White Coat Captioning here all day doing live captioning. Thank you so much, Jordan. And that's made possible through White Coat Captioning, who provides the service, and our sponsors, Netlify, Fauna, and Auth0. All of whom kick in making the show more accessible to more people. Make sure while you're checking things out that you take a look at our schedule. We have an absolutely incredible lineup coming up. So many amazing things happening. Just come and check it out. It's gonna be so good. So many people. Amazing folx from around the community. I love these people. We've got even more coming in. It's gonna be great. So, make sure you go hit that "add to calendar" button and give me a follow on Twitch so you don't miss any episodes. With that being said, what a great time. Tim, thanks so much for hanging out. Chat, thank you for hanging out. We're going to go find somebody to raid. Any parting words before we call this thing done?
TIM: Beer time, on my side of the world, that is.
JASON: Absolutely. Enjoy your evening. Thank you so much for hanging out, Tim. We will catch you all again next time.
TIM: Thanks again, guys. Cheers.
Learn With Jason is made possible by our sponsors: