How to create editor experiences your team will love


Content is a crucial part of what you build on the web. Modern web technologies brings a lot to the developer experience in terms of building content-driven sites, but how can we improve things for editors and content creators? In this workshop you’ll learn how use to approach structured content modeling, and how to build, iterate, and configure your own CMS to unify data models with efficient and delightful editor experiences. It’s intended for web developers who want to deliver better content experiences for their content teams and clients.


Right, I think we have slides. So yeah, we have an agenda today. Let's see how far into it we actually get. But obviously we are in the introduction and we will have an opening lecture with the CTO of Sanity, Simon Svaleskogsru. He will kind of set the stage for what we are going to actually do today. And then we are going to go into content modeling, which is pretty important for editor experience, as well as look at how you can customize these forums to be more helpful and delightful, I guess. And if we have time, we can look into previews and so on within the studio. But let's see. And of course, if you have questions or whatever it is, feel free to ask them and we can try our best to answer them and show. So yeah, obviously this workshop is for you all to kind of like learn and get from it what you came here for, I guess. Any questions so far? Should you kind of actually test out the Discord? Does it work? Where are you all coming from? Like, where are you calling in from? That's a good easy question. I see people typing. Duluth, Minnesota. I guess I can do the same. Let's see, Chippata, Zambia, Tunisia, Norway, London. Carolina, North Carolina. Yay! Cool. As I wrote in the top of the channel you're all in, hopefully now, to follow along in this workshop, and you can kind of tag along and co-op with us, and you should start by installing the Sanity CLI. By the way, Lauren, do we want them to rather clone the repo? Yeah, we can do that. It definitely has the full code in it already, but I think that that's fine. We can give people the option, like they can tag along with our coding, or they can cheat and just download the finished code, I guess. Yeah, either is okay. The repo in Discord. Otherwise, if you Sanity in it, you want to choose the e-commerce. Yeah, it will be for options for a starter CDO template, and e-commerce is the one. We can drop these details in the Discord chat, and if you hit any problems, please also let us know in Discord, and we will try our best to resolve them before the opening mature is done. So, any questions so far? It seems so. Sorry, so you said the final version will just be available at the end? Yeah, Lauren just dropped the link to a GitHub repo, where the finished code is, so if you want to have to have a peek locally, that's possible. Right, I think I will start with the video. I think it's like 12 minutes or something. I'm Simon. I am the CTO of Sanity IO, and thank you for your interest in today's topic. So, I want to talk a little bit about authoring experiences and what really matters about them, and to do that, I want to go back to my beginning to this old machine, the Commodore 64. So, this is what I got in like 83 years of playing. I was 10 years old, and I wanted to make and I wanted to make games, and to do that, you had to kind of deal with all of this. This book is filled with these kind of inscrutable diagrams and maps, and it explains all the different chips in the machine, the SID chip for sound, the VIC chip for video, and then you have to kind of use this kind of IO chip to figure out how the joystick is moving, and like actually, at the back of this book, they put in the whole connection diagram for the whole machine. It's kind of beautiful, of course. You don't do that anymore, but also kind of scary because this is not irrelevant if you wanted to make a game. So, even if what you're trying to do is to make an exciting game about kind of mythical monsters and heroes, this is what you're going to do. So, if your weekend is about like, ah, this time I want to try to make sure like the characters move in front and behind each other in the right way. So, now your weekend will be about creating a sorting algorithm in 6502 machine code. This is a language where like multiplying two numbers together looks like this. It's like 15 lines of code just to multiply two small numbers. So, of course, what you are going to do is going to be a lot about the medium and very little about what you're trying to do. So, it's a little bit like building kind of a ship in a bottle. Like you're going to spend all your time getting it in there and very little time on the actual ship itself. So, that was kind of exciting at the time. It was all we knew, of course, and kind of figuring this out was kind of interesting, like a puzzle. And, of course, people overcame all of this in a sense, like some incredible games came out of that. And the whole industry was basically invented in this mode of kind of machine code and assembly and everything being very, very technical. But, of course, also this meant that there's a certain kind of people who do this, who are kind of enabled to do this, because at that time the teams were really small. Typically, you wouldn't have like an artist or like a specialist in anything but like machine code. So, all these other talents would be secondary. Like you would have someone who knew machine code who also could draw like a cool egg in like two bits pixel style. And that was how it would be for like a decade, I guess. And fun and great, but also maybe we were missing out a little bit. Because we could see that when in the 90s the teams were growing a little bit, it became customary to kind of as part of your development, you would also create tools, like authoring tools would be part of the work of creating a game. And, of course, this was to enable your teammates who now are specializing in kind of creating, let's say the art of creating a shooter landscape or level. You have someone who are experts in thinking about the tactical opportunities, the flow, the visual style, the excitement of like a first person shooter area. Maybe they don't know how to make like a sorting algorithm, but they really know how to create that kind of exciting flow and that kind of experience of always being vulnerable while you also have the upper hand somehow. Maybe those people, but now keep looking over there because you will be shot from there. And that's a specific skill set. And this created a whole new kind of subculture in the gaming culture because now you have modders who are modifying games, who are creating these modules. I don't know if modeling is about modifying or modules, but I think both. They create these content packages for these existing engines. And some of these kind of ended up being completely new commercial games, even though they are just kind of new content packages for existing systems. And that's clearly kind of enabled a new kind of creativity and a new kind of kind of person to be a part of this. And now, of course, we have systems like Unity and Unreal Engine, which is kind of a game engines and offering tools kind of integrated into one. And we are getting completely new kinds of games like indie games now are such a wide variety of experiences. And like this game, the Untitled Goose game is a kind of game we definitely would never have seen in the 80s, right? So authoring really matters. It really matters in terms of what kind of experiences you will end up getting. It's not just something to be overcome. So when I started working with websites and digital experiences as a grownup, I did not make this connection at all. My kind of ethos was I want to make sure every dollar gets kind of on screen, that the end user gets to enjoy every bit of resource that I spent. Visible value is my thinking. And my content creators would get scraps. I would not waste any money on content management. I would say we'll pick something off the shelf if that is kind of really if they insist. But usually they will get like a spreadsheet, some markdown files, some YAML files, and they will just have to make do, right? Because we want all everyone wants to spend all of their energy towards the end user. So I was being frugal, effective, I was thinking. But then you see this kind of backsliding effect. So you made all of these awesome kind of experiences and this expressive kind of solution that you want your content creators to use to kind of delight the end user. And you've given them all these secret codes, all of these kind of weird stuff to put in their YAML files to kind of trigger these kind of beautiful things you made, spent all this money on. And in the beginning, they do that. But then, of course, something breaks suddenly, maybe even. So they kind of backslide. They use like a subset of that experience because they are just trying to get their job done. And they like what you did, the delightful stuff, but they can't afford it in terms of their time to use it. So that's when we made the connection. It's not really about kind of that front end in isolation. People are coming, of course, for the content. And they are coming for the experience that is kind of crafted by the content creators. So I need to shift my mind a bit. I'm making this connection. I'm making this kind of experience, the front end, the thing that people see and engage with, then if that is not connected to the content creators in an effective way, that goes to waste. So any weak link in that kind of connection from the kind of content creator to the end user lessens the entire experience, doesn't matter, like my awesome 3d effects or whatnot. So we started being kind of mindful of that, mindful of the content authoring experience. And our thinking was like, after trying a few more kind of traditional CMSs, we felt like they contain so many assumptions about what's important to the content creators. They contain all of these kind of preconceptions about pages, templates, I don't know. It felt like I'd spent more time taking things out, making sure what's in the content authoring experience is relevant. I want it to be about what is making a difference to the people who create the content. I don't want it to be about anything else. Freedom is kind of expensive if it's not about what we're trying to do. So that's why we made sanity, essentially. We wanted to make sure creating content authoring experiences that are specifically tailored to what makes sense to the content creator is reasonably simple to do. That is something that get done because it's not expensive. It's not super hard. It's a lot to do with figuring out what makes a difference to your content creators. And it might be very different. There is no kind of single approach. Sometimes it might be very visual or very literal. Like it's someone creating content, maybe let's say kind of a landing page. Yes, it's going to be like literally pages and modules maybe. And these modules will have like, they will be calls to action and pricing and testimonials and case studies. So it's a very one-to-one experience. But still I will like, if I then think, okay, I'll give my content creators like a visual tool, like they can do whatever they like. I'm not helping them because not everything makes a difference to them and not everything is relevant to the goal at hand. So now I kind of given them a lot of work that's irrelevant. Now they are moving a module. Now they broke the page because they forgot to put in a divider and it needs to be the right divider. And oh, now the background is the wrong color because this is supposed to be an enterprise product. So now we should use the blue color. So what we need to think about now is like, how do we make sure everything in the authoring experience is what makes a difference to these people? In a different scenario, let's say we are a real estate developer and maybe we don't know which pages will kind of end up organizing this content. And maybe there will be several because like we have our own webpage for our real estate kind of empire, but then maybe people are not actually coming there because we are actually listing these kind of estates in different kind of partner sites. And that's where people actually are. And we have no control over those layouts. We just provide these kind of data streams. And maybe we print out leaflets that combine a lot of these pieces of content. So maybe when we are working in this company, we are thinking as content creators about developments, about buildings, about flats, about areas and the quality of life experiences you can have there. And the demography is that these are relevant for it. And that will be the content model. So our thinking when we are kind of creating this kind of strong connection, this bond between content creators and the end user is making sure this pipeline is about what makes a difference to these groups and everything else. We just make sure we automate that. We make that just go away. And today this is the topic. And I really appreciate that you care about this. And I think there's a lot to do in this area that has not been tried yet. So thank you and have fun. All right. So we probably could take him in to ask you all how does this relate to your professional lives? Is it something that's recognizable? Any reactions to different points that Simon raised? Feel free to do it on sound or comment in the chat if you prefer that. I mean, right now I'm being tasked with getting a CMS set up and react for my company. So I definitely understand the needing to make it easy for people who don't know how to write code letting them like edit a website, add blog posts, stuff like that. Yeah, I would suspect that most of us in this virtual room are probably tasked with something similar. Like that you're making, probably making mostly websites, I would guess. And you need to make it possible for someone to actually change the content on that side without you having to push some code. Right. Right. It looks like someone's typing in the chat too. The anticipation. But yeah, I like to collect the analogy to game design. It's not super obvious when you think about CMSs and so on, but it is kind of like the same thing. Yeah, so in the chat, we need a simple development tool to give our clients the possibility to manage content for the apps we develop for them. Is that kind of like actual mobile apps or web apps, or both of them? Both. Yeah. So there you go. I agree with that. Hi. Yeah, so let's dive into the structured content first. Because if you're going to make something that's people edit content for both apps and websites, then I guess it becomes super obvious why structured content is a great thing. Because those are two different mediums with quite different constraints and possibilities and so on. And for a good while, I think web developers have become used to thinking about the website as a collection of pages and maybe posts. Those are the default content types you get from something like wordpress and so on. And if you look at a lot of developer tooling, you tend to see the pattern, like you make pages and you have page builders and so on. And structured content is kind of like challenging that a bit by asking you to think about your content as this decoupled separate thing in itself and not something that's only presented on a page. And Simon touched on that when he talked about calling this real estate company, right? Like they can see their world through projects and developments and so on and services. And all of those things can be content types. I also think that's just sort of the reality of the society we live in too, right? Very rarely do we just live on websites, right? Like a lot of our information is on other platforms, like social media and like searching. And even in the physical world, they can be on kiosks and all sorts of things. And if it's the same information, it would make sense that you would want your content to have its own model so that you could deploy that from one place and have it go to, you know, any interface that you want, right? Whether it's a website, an application, a kiosk, or even like print, for instance, right? I think like as developers, this is kind of like preaching to the choir a bit because we are used to, or probably used to the idea of a single source of truth, right? And not repeating yourself. And what's weird about content and content on the web especially is that like it's a couple decades ago, we started to kind of like get databases with all of the awesome things that databases can do. Because for content, for some reason, we kind of like stuck it into databases sometimes, or that files, we don't get any of the value of a database. We can't query it necessarily because it's only, it's html inside of a cell or something, right? So structured content is kind of like a way to get more possibilities from your content because you can start treating it as data. And I think we will touch on that a bit today, but that creates for a lot of opportunity and makes it possible to actually use the same piece of content in both an app and a web page for a voice assistant and so on. So it's pretty useful. All right. I'm sorry. I guess for some reason, I'm like, yes. As developers, we want to create structured content, but we hate implementing the tools to manage it. I hope I've resolved the thing now. Yes, you're back. Just unmuting itself. Yeah. Yeah. Sorry about that. Yeah, exactly. We want kind of like that database experience on our end, but it's like the tools haven't been there to actually make that happen. Of course, we are here representing sanity and that is like what sanity is all about. And hopefully it doesn't suck anymore when you use this tool. But yeah, I kind of like skipped through the slides here a bit, but like, I think if you have ever set up kind of like a data model in the database, this would be somewhat familiar to you. But it's kind of like, sometimes it takes kind of like a shift of perspective when it comes to content, because you also will meet requirements from your teams and organizations like, yeah, we need marketing landing pages. And then you can ask, yeah, but does it, we need to kind of structure it as marketing landing pages, because what they consist of is mostly product information and this type of information. And when you start to kind of like see the domains of what the content is actually built up from, then you can start to kind of like tease out content types and like embody them in the system. And that will also make it more intuitive for editors to like find wherever they want to change something, because it makes sense that product information lives in kind of like the product content type and not the product pages folder. So yeah, so this is kind of like a foundation for, I think, awesome editorial experiences. And it also makes it possible to have a cleaner interface, I think. Are we now getting into code? Yes, I think we should. Let's see if I can share my other screen. I'll try my best to actually look at the chat, but Lauren, if you have an eye, you can let me know if something. I will, I'm watching. Yeah, right, so you can follow along. I think I put the GitHub repo in there, or if you spun up a new studio, should have the e-commerce data set in there. Yeah, so this is the repo. I guess I should maybe just spin up on you. Yeah. Let's see. So hopefully you all have done this. Oh, just uninstalled the CLI for some reason. I was testing something out, but um, react, advanced, literature. So while we're installing, Sanity is, we call it the platform for search content. And what it actually is, is a hosted real time content lake. We call it, it's kind of like a document store. And then you have this open source Sanity studio, which is kind of like what you, you can think of it as the CMS that you're building. And the studio, it's a react single page application. So it's only like a client side application. There's no server component to it. So it will load in your browser and it will connect to the hosted backend. So that means that you don't have to fuss around with the database and remote and local and so on. It kind of just connects to the hosted data store. So, and everything in it is real time. So that will be just synced in real time. And you can also have different data sets and so on, if you want to have a kind of test environment and a production environment. And there's a bunch of other things also in this platform. You get kind of an asset pipeline. So you can kind of like upload images and have had them transformed on demand. So we can like request a certain width or resolution or format even. And there's a bunch of tooling stuff that you can use to kind of leverage your structured content in different ways. So now I installed the fresh e-commerce studio. Yes, with the sample data. With the sample data, I hope. Yeah, let's see. So now I seeded into the thing. Oh, live coding. It's hard. Let me see if I can. Oh, did you upgrade to Monterey? I did. I lived dangerously. Right. So this is the fresh studio. And if I run sanity start inside of it, I'm sorry, I have to do this. There we go. Then it will start the development server locally. So we can start changing it. And you'll see kind of like what's going on in this side of the editor. Maybe can you zoom in a little bit? Like this? Yeah, there you go. I don't have, it's just kind of a small MacBook. But let's open this link, localhost, further free, further free. I'll log in. Let's see if I have actual data. Yeah. Yeah. Yeah. Let's go. So this is kind of like the studio and it's pretty bare bones. That's the whole idea. You shouldn't have more things than you need. It should be the only things that's relevant sort of. Because what happens with a lot of CMSs when they mature is that they keep adding features to them. And some enterprise customer had a request and suddenly you have like two more buttons that you never use. So this is kind of like, here we have thought like you should configure it and put in only what you need. And then it will be yours and hopefully it will be more intuitive and cleaner. That being said, there's a lot of functionality already in the out of the box experience. For example, I guess I should invite you, Loren, but I'm like, there's a review changes. So everything you do in a document will be saved to history. So you can roll back. So here you can see there were some changes from before. So it's kind of like if you are familiar with changes in Word or Google Docs, it's kind of like the same thing. So, Loren, what was the first thing? So the first thing was, I guess, well, the first thing since you touched on it a little bit is discussing sort of overarching document types, which you were just in one, which is products. Product is sort of a main standalone piece of content. And so you can see product is a big standalone piece of content document type, and it has field types in it. And that's where you saw sort of the, it can have a revision history. It can have queryable references. But it doesn't, you typically think of these maybe as like pages or posts, but they don't have to be. As you can see, these document types are product, vendor, and category, because this is an e-commerce. It's an e-commerce content model. But let's get into talking about some actual basic fields or primitive fields. Right. Let's do it. So something that's different from a lot of CMSs is that, like a lot of CMSs gives you this UI where you can kind of click and click and click and build forms inside of the UI. We have done it a bit differently because we, in our opinion, a content model is kind of like, you're making the api, you're making the data model that you're going to program towards. So it's like developers should be included in that equation anyways. So it makes sense to have that in code, because then you can also version, track it in Git and so on. And having it in code also lets you do customization more easily. We will touch on that when we talk about validation, for example. Then again, we wanted to make it really easy. We wanted to make like, you should be able to bring your front end skills and make this happen. So everything in Studio is react and javascript. And you don't even need to actually use react that much unless you customize it. You can get along with pretty simple javascript. So in the schemas folder, you will find this schema.js. And this is where you import all your schema files. You could have just written everything here in line, of course, but having files makes it a bit more organized. And then you import your fields and content types and so on. And you insert them to this array of schema types. And the reason we concat them is that because there's plugins and stuff that have their own schema types. So they are automatically brought into this schema types variable. And then you just add your own to it. So that's the basic logistics of how this works. And as Lauren mentioned, we have this product schema. And if you go into that, you see this relatively simple but large object. It has a name. So that means this is something that you can find in the final data model. I can show you the connection. So if you name a document type for product, that means that all the product documents, if we inspect the JSON file, we'll have this. Let's see. Type, underscore type, product. So if I change this to, I don't know, package, it would be type package. So that's the connection. And then we have fields, which are these ones. And those are also just added as object configurations inside of this field array. So we have the title field. And that's called name title. That is setting the key of this data. The title is the UI title. There's a little title here. And type is what type of field there is. And we have a lot of built-in field types in Sanity IO slash docs. You can find them all under schema types in reference docs. And you can see that we have array, block, boolean date, document file, and so on and so forth. So that's the basic gist of how you build forms and how you build document types or content types. So were you saying that it's not possible to set up forms in that front-end interface, or it's just advised that you kind of do it in the code as opposed to that? Yeah. So there's no place inside of this interface where you can just drag and drop them. So this is how you do it. So let's say if I wanted to have a post type, then I would just add a new file. If I can even add a new post. And then I would kind of like write name, post, type, document, title, post. And I guess field would be title. The string, there's a kind of like a shortcut. You don't have to type in title. It will kind of make an effort to just make that automatically from name, but this is kind of like a minimal post schema type, I guess. And then you would import it like this, post from post like this, post from post, and add that to this array. I was saved and you can see it at the studio we just rebuilt. Then you have this post type. So it's not a lot of effort to kind of get that going. And what happens when I write inside of this video is that this content is now instantly available on the api. So this is now synced to the hosted backend. And I can kind of prove that if I publish this, I can go in. We are not going to actually do this, but we have made a query language for JSON. It's kind of like a generic query language. So it's more capable and so on than graphql. We also have graphql if you want that. But here you can see kind of like the document I just made from the public api. Real time. Yay. I'm sorry. So that's how that works. So now we kind of set up a new document type with kind of what we call a primitive field type. Like a string is kind of like pretty simple. Yeah. I'm trying to look at the notes and like at the same time. So we can do like, why don't we do like a short description or something? Because then we can show like how you can limit like by row or something when you want to limit the amount of content that should be in a particular field or something before we get into validation. Do you know like a short description or something like that? So let's say we have an excerpt. We can write that thing. Typically though, you want those to be a bit longer than just this single line. So we can go into documentation and find that we have a text type and that's just kind of like plain text, but it will give you a bit more space like this. But now we're thinking if you want to have something in between, right? Yeah. So if you go into documentation again, we can see that there's properties and properties are kind of like the keys you can put inside of these objects. We have something called rows. So we can control them like how many rows there is. So let's say five. It's smaller. I'll also turn off Grammarly. We don't need that now. I know Grammarly is so intense, but we love it. Yes, got it. I'm with it. I'm finally like putting the docs links in here. And like if you want to have kind of like, if you want to make it possible to hide posts for some reason, you can say hidden and insert a Boolean, for example. And that's another primitive type. And this is kind of like pretty basic stuff. But to make it something that's actually maybe helpful for editors, maybe you also have like organizations that have kind of like a ton of voice guide or content guide, you can start to bake that into the interface. And if you want to do that, you can do it in a lot of different ways. A ton of voice guide or content guide, you can start to bake that into the interface. So a pretty typical thing to do is to say like, add a description. And you can say something like, make it informative. Remember, this is important for seo. Like, seo is pretty important for a lot of organizations. And it can be useful to kind of remind people about that. There you can see that this description just came up. The same title, like what are the best practice for title length for seo nowadays? Short and disc, it's usually short and somehow short and still descriptive. Yeah. Cogency. Yeah. Depending on where you work, it can, you can allow yourself to be a bit human in this. Yeah. Set this to hidden if you want to preview it, or if you have some kind of preview mechanism. Right. But it's also just one of those things too, where if you have a specific way or, you know, system in which, you know, your specific organization does things, the description is also a way of like putting, building in instructions and guidance for editors and best practices, not just like, oh, reminders about like seo, but specifically, remember, this is how we do this specific thing. Like, that's why this field in particular, I think is super helpful. Right. Yeah, I agree. So this is kind of like pretty basic stuff. What's pretty neat about sanity is that we, you're given almost like, you can express pretty much anything, I think, because we also have some more advanced content types or field types. Namely, we have something called object that lets you kind of like, yeah, make an object kind of thing. Like a lot of CMSs give you kind of like a JSON type, for example, to paste JSON inside of it. And that is not super editorial friendly. So we have tried to kind of like make it possible for you to kind of like express these more nested and complex structures that you may want to have, either because it's kind of like a requirement or the data comes from elsewhere, but like translate that to reasonable offering interfaces. So what would be a good object structure to make here? I'm assuming you're asking me. Yeah. Well, I think since we have posts, a good one could be like authors, because we already have a person, right? Don't we have a person post type? We didn't make that yet. We don't. But we can make a person, right? Yeah. And this is also an example of how you can kind of like develop content models pretty efficiently in this way. So let's make, yeah, this, by the way, this is the classical kind of mistake. You start off by making an author field, right? But let's just do that. Author, we know that an author has different properties. And bear with me, I'm just doing it in line of the post now. Object, an object has fields. So this is kind of just the same pattern as we saw with document. So they have a name, I guess. Yes. That's a string. Are there things we want to embellish the author with? Maybe like a bio. Yeah, that's a great one. I guess that can be text. What else? Maybe like an image. Right. Okay, we can call it photo since it's an author. Yes. And that's probably good. It's in line, so we don't need a slug. Yeah, it's in line, but that means that we would have to fill in the author things for each post, right? Yeah. And there might be the odd chance that it is the same author for a lot of these posts. So it would make sense to kind of like, I don't know, have this as its own content type, I guess, or document type. Yes. Yes. So let's say, okay, we found it makes sense to do that. So let's just cut this out of this thing, make a new file, call it author. And we can make this a document type. And then we can go into the schema.js or author from author, add that to the array, save. And now it should appear as this offer document type. Let's go to unsplash and find a person. Let's go with her. Yeah, she looks great. Oh, this is an ice dock. What? What? It's weird. Oh, right. Let's just copy this image and paste it. Right. And this is one of the things I like with Sanity that you can just kind of like copy and paste an image and you don't have to download it and upload it and so on. It's pretty neat. Right. Now we have an author. But what about post, right? What we can do now is to add something called a reference field. And this is where structured content really comes in because you can make these connections between different content types. Let's say, let's still call it author. By the way, no, let's wait. Let's use the reference type. You can find it here in the documentation. And we can see that there are three required fields type name. I have those and two because it needs to be referenced to something. It should be referenced to the document type called author. So now we can see that we have this new field that gives us kind of like this dropdown and we can choose this author. And if you look into the data, you can see that the author has this type reference. This would be the document ID of the author I just made. To show you how this works, if we have post, now I got to connect both the draft and the published post. Just let's make that, just publish this post to make that a bit cleaner. Then you can see that we had this author here, but the information isn't useful. So we can kind of prove that it works by joining in that data. And then here you can see that we have the author documents. We can even do stuff like selecting only the field we want. And what's really cool, I think about objects is this was a sort of quick, you know, use case. Use case, but on a grander scale, you could have had like an actual person document type and fed in all of the person data into, you know, this author thing where you could just choose all of the people, because not all people are authors, but you want to have all of those people available as an author. So that way you don't have to like, you can just kind of transform data that you already have into something else. Yeah, that's a pretty good point. Like authors, it's probably a bit too specific, right? Okay, so I'll just do that. Oh, you don't have to change it. Okay, let's leave it there. So the mistake I referred to is that we tend to assume that there's only one author, and then it'll go two or three weeks, and then there's the thing where you need more authors, right? Because someone wrote something together. And that's where the array field comes in handy. And this is a repeatable field is sometimes called. So what you can do then is to just move this author reference field inside of this array that has an array of like types. Let's see what happens. Here we got a new field, and we also got this unknown field found, because we removed the field. It doesn't delete the data. The data is still there, but the studio says like, I don't recognize this data in this document. And this is kind of like pretty normal when you And this is kind of like pretty normal when you do this development. Ideally, you should maybe have planned out that content model a bit more beforehand. But if you just want to kind of like spur like this, that's also certainly possible. This is pretty easy to to kind of like recreate, because we just have one document. But if you had like 100 documents, there's a pretty simple way to script that and run a migration script and kind of like move that data around. We won't go into that now, but yeah. But I will just remove this field for now. And then we can go in here and we can enter like this one reference. You can also do like this. It kind of doesn't make sense, but it's a good showcase to show you some validation after. Yeah. So this is the array field. Array field can kind of like hold also complex fields or object fields. In this case, it's a reference field, but we can put in anything and you can also have an array, but an object field that has an array and so on. You can go and nest that down and this data should be able to adapt. Yeah. Right. Yeah. Hope folks are tagging along. Maybe. Where are we? Should we go into building out the image for a little bit? Yeah. Let's do that. Do you want to do like the logo one? Or do you want to do alt text and caption? We can do alt text and caption on the author photo maybe. And then we can also show hotspot. Okay. Right. So we have this photo and if you like something that has become or it has always been important, but now it's kind of like important for more reasons is that all photos and images on the web should have alt text. If you don't have it, Google will punish it for you and you will also exclude a bunch of people who can't necessarily see the photo that you have. So it's kind of like good practice to always have alt text and you sometimes you also want to have the option of actually having caption, right? And those should be possible to make those different strings. Sometimes an alt text is more descriptive than a caption and so on. This image field, it's called image. It's kind of like an object field with some special properties. So we can actually add fields to it. So I can say alt and that's string. If I say no, there should be another button appearing here where I can add this thing. And if we want to caption, we can do the same. And then when I do this, I sometimes like my editors would say like, I wish those fields were more easily accessible. So if I want to make them more easy to get to, I can add this option thing. And a lot of the built-in fields with sanity has an option with different kind of like configurations you can have. And one of those is highlighted. It's not the most obvious phrasing. I can type when I'm live coding. Let's just go and copy paste. Here we have, oh, haven't we documented that? I don't know, but I can spell it for you. No, of course. Yeah. It's kind of like in the fields options. That makes sense. So now we have oh, options. Yeah. Well, now we have these fields here. And it seems like an actually need to have title here. This is a small plug that we found. Right? That's funny because I guess I've never done it without putting the title there. Yeah. Yeah. There we go. So now we have these two. Okay. And then sometimes you or a lot of times you want to be able to give authors or editors the option of like choosing the most interesting part of an image. So you can also add options to the kind of the image fields itself. So we have something called hotspot and true. That will give us another edit details. And then we have this thing where we can kind of like choose the, I guess the face is the most interesting part of an author image. We can also kind of like have some cropping if you want to have more control of our direction in the front end. And when I do this, it will create these coordinates. So you can use in, in, in your front end code. We also have tooling that makes this a bit easier to implement for you. And here you can see that the preview also adapted to the And here you can see that the preview also adapted to, to these changes. And just to show you kind of like pretty neat affordance for editors. Here are all my changes. And here you can see that I inserted a photo. And if I now go in and change these things, I think, I hope this works. You'll see that it's kind of like there's data about that. We won't go into this today, but everything here can be customized. So if you want to make like your own input component, you can do that. You can even make like your own diff components. So if you want, if you're not satisfied with how we show this difference, you can like insert your own rec component. And we will have these studio have APIs that makes it easy to access these different values and so on. So that's just good to know if you have those needs in the future. I feel like we are closing in on validation now. Yes. Right. I was just going to say, I think this is a good place to do that. What do you want to do first? Well, since we're here, let's make, let's make this alt text because that's, I think it's a standard one and not custom. Make it required. Yeah. Yeah. Right. Let's do it. So validation, it's a pretty huge subject, but you put it in by adding this key called validation. And this takes a function actually. And this is where kind of like the javascript nest of this starts to kind of like make sense. So we have a bunch of built-in rules. So one of them are is required. No. Yeah. It's just required. Yeah. Like this. Oh, I'm sorry. It's rule. Rule. Yeah. Singular. So here you can see that now this will complain. Like this is required. You have to insert it. And if we want to be more helpful for editors, we can explain why. Again, we can go into validation, look at like docs. And they tell you that you can actually insert a string into this method. So we can say, yeah, this is required to make your content as accessible as hard to write. There you go. Good job. And then you have kind of customized this. Oh no. Did I, yeah, I forgot it's error actually. Yeah. So there we go. Right. And if you want, sometimes that can be a bit impractical that we are actually kind of like blocking publishing this document. If you haven't anything here. So if you want to relax that validation a bit, you can make this a warning. So now it says like, there's a warning. Like you should, you should have this, but you can still publish it even though we haven't. And we are working on, I think there will be kind of like an info method as well that just kind of gives you some info on a certain condition, but it's kind of like even more relaxed. So that is kind of like a pretty, fairly simple validation rule. We talked about titles and seo, right? And I should, I should have made my research. There's actually some best practices when it comes to how many or how few words and so on. But validation, another thing is max. So let's say 80. I think it's a min 20. I don't know. And you can call like, attach these to each other. Here we go. Like must be at least 20 characters long. Yeah. And you can put warnings in there too. Yeah. Hello world, I'm Galax. Right. Um, find the optimal length. This is longer than it should be probably. I guess 80 characters is a lot. I don't know. Yeah. Yeah. There we go. You can also use it for things like slugs too, where you obviously would want to have like a, you probably want to have a max. Yeah, exactly. It's also possible to kind of like add an array of different rulesets. Um, I wonder if we should show, yeah, here we go. Like we have, like, here we go. Like here we have, um, copy pasting is awesome. Uh, if I add an array, here we have like two rulesets, one that actually gives you an error. If you have too few things, right? So like, um, hello. That's not enough world. That's enough. Right. And then we can have a warning if it's too long. So you can get pretty fine grained with this. Um, other examples is kind of like a custom, uh, validation. Um, we don't need to live code that, but it's kind of like, you know, um, we don't need to live code that, but just to give you a sense, I will zoom in a bit. So that will give you kind of like a function callback where you can access different things in the, in the fields. You can have the field value, but you can also have the document or parent field values and so on. So you can kind of like make validation rules that like, if this other field has this value, this field can't have this value and so on. Um, and that's where javascript having kind of content models and these things in, in javascript really makes sense because you can kind of tailor make exactly the things you'd need to have there for the case you have. Um, a lot of kind of like editorial experience is, is about actually automating and capturing those organizational rules or ways of thinking into the system. So you can kind of like enforce and help, uh, help others do it. And the last thing I want to do, like we have this situation here, we have the same author in these two fields and that doesn't make sense. So we can add a rule for unique. I think it has been a while. Oh, here I go. Yeah. There you go. Yeah. It has been a while since I've, um, it does not work. Some of these things has been a while since I've done. Uh, so, um, stay with me. Okay. See validation. Oh, it is, it is real unique. Oh yeah. I put it into the wrong place. There we go. Okay. I was like, it's definitely. Yeah. So here we go. Can't be a duplicate. Right. So long we can delete that. So that's also pretty useful. All right. Yeah. And again, that, again, I keep bringing up the slug, but that's obvious. You would definitely want to use maybe a validation, a unique validation there, perhaps, depending on what you're doing. But slug is usually a good place or something like that. Yeah. Uh, I think even, I think the slug, the slug is also kind of like somewhat special thing, uh, or a kind of own field. Some, some CMSs will just kind of like, or just use a string field for your slug. Um, but we made a built in type called slug. Oh, that's right. Uh, and had some options. So if you just like do, oh, I'm doing something wrong. Oh, I forgot. Yeah. Um, so this is kind of like, it looks like a string field. Um, but you can see that the, has this kind of like object structure. And this is, this is future proofing to support historical slugs. We don't do that out of the box now, but like, that's why. You can also set it to have some options. So you can choose a source, for example. So if we would choose the title field, then we get this generate. So you can kind of like slugify this field. Um, and it also has this is unique property. So if we want to kind of like make sure it's unique across all your posts, we can, uh, you can add this. Yeah. It takes a function, right? That's, that's right. So there's an example code here to actually make it, uh, to, um, to, to make that happen. And you can also insert your own slugify function. If you, um, if you want to do that, if you have some custom, uh, requirements. Right. I think we are halfway in. At least. Yeah. Yeah. Yep. Yeah. Cause I think we go for another hour. Yeah. Yeah. So any questions so far has just been kind of like possible to follow. Are we going to cover later in the video, uh, showing how this, these objects go from this to being placed on the page by developer? I didn't plan to come like show how to implement it because it's pretty much, it's pretty much getting kind of Jason data. Okay. Uh, if, if like, if we have time, I can kind of show you maybe like a mini proof of concept in next or something, but it's, it's pretty straightforward. So if you like, if you connect to this via the graphql api, it's kind of like the same data that you get up from a graphql api with fields and values. If that makes sense. I don't know too. Yeah. I don't know too much about all your backgrounds, but, uh, but it's kind of like, what's what you are used to in the react world. Um, everything is Jason. Um, so yeah. Uh, right. Um, um, should we go into initial values? I'm looking at you, Lauren. I know. I think so. Yeah. Yes. Let's do that. Let's, let's look at, um, vendors and we can talk about more field options. Right. Take me for it. So let's say, so in vendors, if you have vendors, for instance, maybe when you're coming up and you're thinking about what fields should go in your document type, for instance, and what your content model should ultimately look at, content model should ultimately look at, look like sometimes in terms of editor experience, you might want to build in some fields for consistency or that help consistency. And one of those things might be, let's say your vendors have specific regions, right? Well, that's not necessarily a string field that you would want people to have to type in all the time because, um, regions don't really change. If you have vendors, they probably come from, you know, if you have six regions that your company deals with, they're not going to change. So you probably want that to be something that, um, an editor or content person just selects all the time. And that way you don't have to deal with inconsistent data of, you know, upper lowercase things, misspellings, all of those things. So then you can build that string field, um, to be, you know, sort of a dropdown instead of just, uh, a text string. So we're going to show you how to do that. Yeah. Um, what's useful when you kind of build these fields and so on is to start by thinking about the, what is the data type I want to end up with. Uh, and this, in this instance, is actually a string, right? Yeah. So let's start there. So, um, it was region, the type was string. But as I said, uh, you want to kind of constrain this to just some, some specific options. Um, and what this string pill has in its options, um, configuration is something called list. Um, here we can build a list of three defined values. Uh, now we'll do a bit of cheating and just copy paste it. Um, but like that, um, we're wrapping here is awesome. By the way, this studio is responsive. It also works on, on your phone. Um, so here we have this list. Um, this springfield was transformed to this dropdown. There we go. Oh, you have kind of like constrained it to only be, uh, these regions. And here you can see, you can have collect a title. This is the, the editor view. Um, you can have collect its own values for your data. So you can say, for example, United Kingdom. And so this says United Kingdom, but it's UK in the actual data. So now we have consistency. Uh, I guess like, let's say that your e-commerce usually have vendors in a certain region. Like most times it has kind of like, it's, it's, it's, um, what, what region should we go for? Um, I mean, probably like the UK, right? This is like a line of base conference. Yeah, let's say UK. Like, like that is like in made 98% of the times the vendor will be from, from the UK. So let's automate and save our editors some time. And we can do that with something called initial values. Um, some people call them default values, we call them initial values. Uh, so you can add this initial value property to this field setting and you can say UK. And that should work, I think. Oh, and what happens now, if you make a new, uh, vendor document, you can see that this field is pretty defined as United, United Kingdom or UK. Uh, it's also possible to do this on the document level. So if I remove this, and here we can see that this was also removed because now it's kind of in this new document state, you can say initial value, and then we can just kind of like insert the, the object data. So you can say region UK, and this will be, this will work the same way, uh, it's maybe plural. Oh, it's just me mistyping it. Yeah, there you go. Um, yeah. Uh, but then you can also say title, uh, vendor. I wouldn't usually recommend having initial values for string fields because, um, it's easy to forget to change them if they should be changed, but it works. And what's good to know that initial value can also take functions or promises even. So if you want to look at a value from elsewhere, you can actually return a promise to this. So it will kind of like patch something from an api and insert it into the document. And let's say like a practical example is for example, post. Let's say that you like, like low is kind of like, like one, like eight of, of 10 posts will be written by low. Uh, and sometimes the CEO will write a post. Um, but to optimize this for low, we can, we can also put this as initial value. Uh, but this is, this isn't, um, this isn't a reference. Um, so what's, what I usually do then is to kind of like make this initial state, I guess, and go into inspect, and then I will take this object and just copy paste that into initial. Oh yeah. Uh, let's, let's do this first to see kind of like the distinction. Uh, so now I'll put it, put that into the, the author reference field inside of the array. So if I make a new post, nothing is there. But if I say add item, it will automatically be this data. So that isn't exactly what you want, right? We want that, we want this data to be there when we make a new post. Um, so that means I need to move this. I can move it to an array, I guess, but then I just have to remember to take this in an array too. So if I make a new post should be translated to this data. I didn't, I didn't even know you could move it to the right. Okay. Awesome. So the, the, like initial values is, it's pretty powerful. There's a lot of things you can do with it. Um, like you can use, you should be able to, you should be able to, you should be able to you can use the, the, um, the browser APIs as well. Like the browser can ask about your geo position, for example. So if you want to automatically insert the, the geo location of where you are, when you make a post, you can, you can do that as well. Um, I, I, I won't set me up for, you can press me. There is videos. There's a video for that as well and a guide, but, um, um, that's initial values. Are there examples we should show with that? Questions. Something you can say initial value true. If you want to have it automatically hidden. So you don't have to kind of like remember that. Ah. Did we show how you can also set initial values for image fields? Maybe. Yeah, we could. It's, it's kind of like the same principle. Um, um, so we have this author thing. Uh, let's say that we want to have come like this placeholder image. Um, so how I would go about doing that is say, I guess I would go to images, Google images, um, person avatar, um, and find something that's pretty neutral. This one, maybe. Yeah. Um, copy that, paste it into this. Did that work? I think. Yeah. Maybe it's an svg or something. Oh yeah, it could be. I was touting the, yeah, it's, it is svg doesn't play nice with the copy thing yet. Um, anywho, so now upload this image, then I can go into data and I can copy this photo data like that. And then I can paste it into the initial value. So now we should be able to make a new author and it has this preset. Yeah. And we can do like, we can add on bolt, uh, on, um, person. Here we can see that this is pre-filled. Yeah. Which again, like this would be pretty useful again, like because we turned, if we had author as a separate object, because maybe if you're using person data, maybe this author image is a different image from the person's actual thing. So then you could have it totally, like you just have now a different image, even though it's the same person, maybe it's the same person information, but we're just going to change this image. Exactly. Um, now I also see kind of potential because we have kind of like a pretty advanced image field going on here. Yeah. And currently it's inside of author. Um, but it makes sense to have kind of the same settings elsewhere, mostly at least. Yeah. So again, we kind of did this in the start, but I wanted to show it again, just to kind of like give you the sense of how you work this, um, in real life. So now I will just copy paste the whole thing, the whole photo field, and I will make this, um, all that figure or something bigger. That's a good one. Yeah. Yeah. And then I will go and change some of the things. I have the name. I'll call that figure. I will maybe not have this initial value thing. And then I will keep the rest. Yeah. And then I can go into schema and and port figure from figure and that's to our array. Uh, and what I can do now is to go back to the author and I can change this type from image to figure. And this is the name that we defined. It could be anything I could call. I could have called this taco and refer to it as taco. It's kind of like, it's up to us, but it's, it's good to make it something that's kind of like obvious. Uh, and then I will go and delete these fields because they don't need them anymore. I will keep the initial value because I still want this in the context of the author, but I don't want that anywhere else because it doesn't make as much sense. And now I should be able to save and nothing should kind of change. Yeah. Yeah. Awesome. But this is the great thing about repeated patterns, right? Now you can use it anywhere. That means that if you want to have kind of like, uh, um, main photo or main image for the post because we don't have that, right. Then we can go in here and we can say, um, main image type figure title main image. And then we can reuse that field configuration here. So this is kind of like keeping it dry in a way. Uh, and now we have like one kind of like built out image field that you can change. If you change it up here, you can change it, uh, anywhere for defining our own types. Can you say more about that? It's for defining our own types. Can you say more about that? I'm not sure I understand the question. What, what specific is for defining our own types? Yes, it is. Um, so it doesn't need to be document types or always it can be kind of like this specific field, like it can be anything like, okay. If you had like, if you have a string field that you like, you can even import that into the schema JS and refer to it. Yes, I see. Yeah. Yeah. Yes, it is. I get it now. Yeah. Hopefully that makes sense. Yeah. Usually like I try to distinguish in like between these types and like document types is normally, um, like these types of things, like figures you would never actually see in like in the studio. It's just, uh, something that you would sort of reference between document types. It's like a helpful document type, but we call them object types, I guess. Yeah, that's true. Um, like if you, if you kind of like squint a bit, a document type is, it's kind of actually just an object, but it has some, has some special features like it will be listed in this list and the backend will kind of like add some fields to it, like created at and so on, but kind of like conceptually it's, it's kind of like just objects all the way down. Yeah. Uh, because the, the JSON document is an object, right? Yeah. Um, and by the way, uh, maybe we actually have time for that. Like this document list, like these pains are also totally customizable. You can change them and you can like make your own document groupings and you can say, this all products by category and so on. That's kind of like, uh, something you can access in code and change as you want. Um, right. Uh, so now we, like we, we did go through initial value, um, and we have again looked at, like we call this hoisting like, like lifting up a content type, importing it to JS and make it kind of like referable elsewhere. Um, so what's interesting, you know, like, um, let's say you want to have kind of a gallery, right? So you have products. I don't think it has gallery already. I don't think so. Right. So sometimes that you want to have a product gallery, right? Uh, it doesn't even have an image or you actually have a gallery thing in here. Um, oh yeah. And the product variant. Yeah, that's true. Um, um. Yeah. Let's make a gallery and a gallery. If you think about it again, like start by thinking about data types, it's kind of like a list of images, right? So it's, it's an array. And then you can say it's an array of image. Of course, this is the easiest way to make a gallery or a carousel or whatever you want to do. Um, and here you will have the image field, by the way, let's do a small detour because one of the hardest parts of kind of setting up a content system, um, having some kind of like, you want to fill stuff with content to kind of have something to kind of have something to try it out with. When it comes to images, that's kind of a pain. Sanity comes with a bunch of plugins. So if you go to our site, you can find, oops, sorry. You can go to some plugins, slash plugins, and there's a bunch of plugins here. Uh, and you can install them in the studio by typing sanity install, and then the plugin name. This is kind of like just npm install, um, under the hood. So we have some called asset source, uh, and splash. Because you can add your, like, you can add, uh, more asset sources and you're going to even write your own asset sources. If you have like a custom to them or look at digital as a manager, you can like make an integration towards that. Uh, and we have, we made an integration to unsplash because it's just so practical. Um, so now in installed and the splash plugin, and if we reload the studio, so now we should be able to select and splash and then we have a splash and then I can just select this and get a bunch of images pretty easily. But now you just use this image field, right? This is the basic image field. Uh, and we know that we already have a figure, so why not just use that figure thing? So here we have the, all the fields we have want, right? And then we can go, this is caramel. Oops, sorry. All right. Delicious caramel. Boom, there we go. And then I can make sure we have the most interesting part. Um, so here we see that we have this untitled thing going on. So the studio makes the kind of best effort of previewing things that are inside of objects and in lists and so on. It tries to call, is there a field called title? Let me take that and so on. But sometimes it can't find something that's sensible and you end up with this untitled thing. But here we can go like, it makes sense here maybe to have caption as the kind of preview. Oh, there we go. It already found caption. But let's say we wanted to have alt as the preview, right? We can also override how this preview works. Let's say we want to do that for all the figures. So we go back to this figure type. And then we can add a new property called preview. And it works. And there's documentation on this as well, of course, but it works by kind of like making, having you select some data inside of this field. And this is kind of the object field. We have fields here. So you can say select. And then we can map some content to different parts of this kind of like UI element. And what you typically have, here you can see that kind of like because this auto saves when I remove focus, now we can see that the preview disappeared because it's just an empty object. But we can say title. That should be alt because the name of this field is alt. And the media, I'm not sure how that works actually inside of the image field. Oh, that's an interesting little, yeah, we at least got that. Is it maybe asset? Yeah. So that's not obvious, but it works. So that is kind of like how you can map different fields to this preview. Sometimes there's a subtitle available. I'm not sure if that's the case in this view. Yeah, there we go. Like you can also add some subtitle to this. Sometimes you want to have even more control over this preview. You want to do some string manipulation or whatever. And you can also do that with adding this prepare function. And the prepare function takes the select object. So now we have access to title, media, and subtitle. And here I use like object destructuring. So another pattern it would be to do props. Sorry, const. And then we can return again, title, media. So if I do like that, you will see it up. This is the data, right? So I can say full text. Then we can just return these as is. So this is how we can kind of like customize it even more. And again, like you don't have to do it, but it's pretty neat when you want to do it. There's a lot of opportunities, especially if you sit down with your editors or your content team and talk about this. There's a lot of like these opportunities to make this just better and more customized to what they actually need and want. And especially if you have kind of like larger content teams that need to be aligned, you can kind of bake in the direction and so on that that people need to make the best content and so on. Yeah, their whole workflow, basically. Yeah. I actually want to make this. I actually want to make this. Actually, I caption this all. Here we go. Here you go. Okay. Small pro tip, by the way, when it comes to this pattern where we have an array of images. I hope this works. Let's see if it works. You can say layout. I think you can say grid. Just realized. Maybe it works, hopefully. Yeah, there we go. So now you have kind of like a better view for a gallery thingy, in my opinion. If the images are. Yeah. Yeah, makes me hungry. Now it's maybe a bit easier to like reorder. By the way, now I'm pretty satisfied with my initial studio setup. And I want to do two things. I want to make this available for Lo and I want to invite her to this project. So I can still have this running. Let's add. Now we can say sanity deploy. And we can say react advanced London. So now we are like, I'm creating the URL for the studio where, because we, we offer this kind of like hosting up to studio. So that's the easiest way to go and get going. Just type sanity deploy, follow the instructions. And now it will compile the studio to call it the production app. It will upload that to your domain. That's empty studio. So here we can see that it's deployed. This is on the web. It's the same thing. And you can see that my content is here. And then I can go and I can say sanity users invite. Lauren at sentio. Yeah, you can be an admin. And now Lauren got an email and she can go and log into the studio. You don't have to use the CLI to do this invitation thing. There's kind of like a manage interface. So if you go here and you push on this, you can go to manage members and you can do this also in the interface. Alongside like other things, there's a bunch of things you can do with that here. Did you get the email, Lauren? I'm going in now. I should be in there. Can you see me? I see you. Yeah, maybe I can reload. Oh, you probably have to go in there. Yeah, I'm going to go in there. Yeah, maybe I can reload. Oh, you probably have to go into somewhere. Yeah, all right. There I am. There you are. So here's Lauren. All right, can you find me? Can you find me? Are you in authors? Yeah. There you can see Lauren, right? So this is real time. And to answer the question in the chat, the data of the hosted version is different from the one you have in the local version? No, it isn't. So if I go into it, this is the local host version. So hopefully I should be able to find Lauren. Here we go. Hopefully I should be able to find Lauren. Here we go. And here we can see my remote. So this is pretty neat. And again, there's kind of like, you can have different data sets if you want to have kind of like a staging or like a developer environment, if you don't want to mess with production data when you are in production. But it's always hosted, so you don't have to deal with sync and so on in that way. And what's interesting is that Lauren can start working with content, but I can still go into this author document type and build out fields. So let's say I want to have social, right? That's an object. Fields. Sorry. Twitter. I can't write javascript today. Type string. And I need to come up. So now I added this kind of like social links field. I have it locally, but if you go to the remote version, it doesn't, it isn't here, obviously, but it comes up as this unknown field found, but you can see the data. So the data synced, the remote version will have this one. But I can also say that I can kind of deploy it again, and then the new field will come up. Since the studio is this react single page application, you can actually host it anywhere. So if you would rather like to commit the changes to GitHub, you can connect that to Netlify or Vercell or wherever serves kind of like static files and deploy the studio there and access it there. And what's interesting is that you can also have multiple studios with different configurations connected to the same content lake, to the same data set if you have kind of like that need. Because the studio is just an application that connects to the APIs just as your front ends or apps would. So that's another advantage of having this decoupled from the data backend in that way. Because maybe you want to kind of like have a studio where you just have the product like for a certain team, and then you want to have an admin studio with all the things, for example. I can see a question coming up. Oh yeah. I'll be here. We can also see. Oops. There's two questions. It's a race to the finish. And we can also go in here and now we can see kind of like review changes. So here Lauren made some changes and I did. I can even go like do this and it should be able to source out that I can like did this retraction within the rich text, which is pretty awesome I think. And I can choose to revert that. Right. So do we need to have a separate disconnected studio for each different client we have developed the sites for or is there a way to access multiple sites through one studio instance? So there's kind of two questions hidden there. What you would do is to make kind of like a separate sanity project for each of your clients. Because maybe you want to add like you can attach projects to organizations with billing, for example, and typically you want to have your client put in their credit card and so on if they are going to pay for it. But you can still have kind of like this single studio repository and just kind of like connect it like set it up to different projects. If you want to kind of like repurpose the code. So if you look into the sanity JSON, you can see that it takes kind of like this api configuration. So it has a project ID and a dataset. So that means these can also be kind of like inserted with environment variables. So that means you can have one git repository that deploys to multiple like Netlify projects and each Netlify project has the project ID of your client. So that means you can have the same code deploy the same studio for different clients and they can be in different projects with different billing and even plans and so on. So that's one way you can look at it. In terms of driving multiple sites. So if you have kind of localized sites or you have kind of like an advanced organization that has kind of a lot of properties, like we work with Unilever, they have like 26 localized sites over the whole world and like 26 full web sites. That's mostly a matter of content modeling. So there's a lot of ways, depending on what you need, you can make that happen. So you can, for example, make a site document type that holds kind of like all the information, like all the global information of each site. And then you can make kind of like a post that reference that site and you can start kind of like querying the data that way. And you can build out, you can customize these lists to have kind of like all the sites listed out and all the relevant content to the sites. Based on references, for example. So that's more kind of like, not that more advanced, but slightly more advanced topic that we maybe can have another webinar on, but it's certainly possible. And it's all for like, we have tried to make this tool adaptable to whatever you need, basically. So the next question is about user roles. Yeah. So there's some built-in roles that comes on most of the plans. And then we have something called custom permissions where we can get super fine grained with who should have access to what. And that's an enterprise feature. But basically it lets you define like, this group of users only have access to posts that has the main image set. You can make rules like that. You can call it make super expressive rules. So yes, in short, yes. And if you want to make kind of like a home or a homegrown version of that, you can, of course, make these multiple studio configuration and only expose the things you want to expose to certain users. That's a bit more on you, I guess. And it wouldn't prevent them from making those changes through the api if they knew how to do that. But that's kind of like the way to maybe get around that. But it also takes us to conditional fields, I guess. Yeah. So did we have a good case for that? Sorry, I don't have notes in front of me. I think we did. I think one of them was we could show not showing the product variants before there's a default variant or not showing image alt fields before there is an image, which I think we kind of, yeah. Yeah, we can start with that. That's kind of like a more simpler case. Yeah. So let's say, so when you do structured content, you tend to end up with a lot of fields, right? Because you are trying to describe the shape of your content. And a lot of times, the fields aren't always relevant if a certain condition has not, kind of. So you can control what editors see. So for example, you don't really need to see these fields before you have the image, maybe. So let's make that happen. I just wanted to remove that initial value. And I guess also the image. Oh, I'm looking at the remote. There we go. Right. So what I want to do now is to hide these fields if there's no image set. So I go back to figure and then I go into these two fields and I add a property called hidden. And hidden can take a boolean. So you can say true to hide it. And this is useful when you, like we are in e-commerce now. What we like, what a lot of people do is to sync e-commerce data or product data from shopify and augment that with kind of like their custom content. And then you have a lot of fields you don't really want anyone to change because they are synced from shopify. So one way to kind of like make them, hide them from editors is to actually just say hidden true like I've done now. So now the old text is hidden. But this can also take a callback function. So I can say return true. This is the same. This is the same thing that I had, but it's not super useful. The clue here is that in this callback, I get some data. So let's look at what data I get. And I will just log out the data I get. This is probably not. So here we can see that in the hidden callback, I get current user, I get document and the parent and the value. Now there's no, nothing in this document. So let's say Knut. And then we can see Knut. Oops. And then we can see that the document has Knut in it. And now we can start to make logic on this. So what I know is that if I insert a photo, or I can, I can actually just show you, right. So when I have a photo, it will have this photo property with asset. So what I can say inside of this, and by the way, like this also takes something called parent. So we can kind of see the parent field that this field is in and the parent is the asset field. So I don't have to go into the document actually to find what I need. I can go into a parent and I can see that asset is kind of like the thing I want to look for. So I can see if, if asset, if that's true, then I want, then I don't want to hide it. So the logic here is a bit inverse, maybe because like hidden is true, then it will be hidden. If hidden is false, it will show, right. So if asset is true, then this needs to be false. Let's see if we can do it kind of like falsish things here. So if I put correctly, now it should show, right. Yeah. So now it shows because I have an image here. If I remove this, I guess asset isn't completely removed. Oh, no, it doesn't do break. I don't have to do like this. What did I do wrong? No, it's called, isn't it? Oh, yes. So it's props. Good. Good. It's props asset. Good one. No, it's, it's not, it's, I'm now, I'm losing. It's parent. Yeah. So it's parent dot asset. There we got it. Bit too faster, right? Now it should work out. No. No. I think I have to. Yeah, there's something else nested in there. This happens every time we're doing images. It's, it's the demo god slash just live coding is hard because we have to do two things. I think it's, it might be it. Yeah, it's undefined because I'm awesome. I'm straying from what I should be doing. Oh, yeah. Oh, yeah. You have to put props back on too. I have to, I should collect, look at the documentation. Oh, yeah. Because then you will have the member. Remember the documentation. Yeah. Yeah. Look at that. Maybe, maybe this will work. I'm only human. No. No. Oh, yeah. Because I'm, I keep screwing up the object. Oh, there we go. Finally. Thank you. Yeah. So if I now choose this, yeah, there it's go. Yeah, there we go. Um, and the reason we have this value, I think if I now remove this, this stays, right? Because that makes sense. Because this hidden, it doesn't actually remove the data if there's data there. So we want to make sure if, if there's data in this field, you should be able to change it. Right. But if I, if I delete this data, it will disappear. Right. And again, this is, this is just, right. That's interesting. Why does it appear there? Okay. Anyways, never seen that before. Yeah, that's weird. Yeah. Again, this is javascript. You can, you can do whatever you can do with javascript. So if you want to have kind of like a weird regex thing going on or whatever, you can also do that. And for the observant of you, you can also see that we have this current user thing. So that means that you can also hide and show fields based on the, who the user is and what role they have and so on. Again, like if you are using our custom role, like the studio will kind of see that automatically. The role system only works on document level. So if you want to have kind of like additional feed level access, you can use this to kind of like make that more fine grained. And again, it doesn't really, this won't prevent them from changing it in the api, but that's usually not kind of like super plausible. So this is also no way to kind of like hide and show fields based on who is in the studio. So that's conditional fields. Questions? I guess you, if you have questions, you put them in the chat, but I also don't want to kind of like just go too fast. Yeah. And we're a relatively small group, so you can unmute your mic and ask them too, if you would like. Do you want to do the product variant one too, Canute? Sure. Let's do it. So what we want to do is to hide, and it kind of like makes sense maybe in this, because there's a lot of things going on here. So here we have this default variant, then we have variants. Yeah. So you want to hide this field if nothing here is entered in, right? Right. Right. So, all right. Then we need to go into the variants field configuration. Let's see. Hidden. I guess we need to have value, so we can show it if there's anything that has been written in the variants, and then we need to go to document, because these are kind of sibling fields. So if there's a value, don't hide it. Right? Right. And if there's a value in document, and then I use this optional chaining, so we don't get the count, read default product variant of undefined, if document for some reason is undefined. Then we take this field and look for that. And that should be it, right? I think so. Yeah. So now we shouldn't be able to see variants. Nope. Yeah, that's correct. Unless we say, pick lunch with some Norwegian chocolate. There we have variants. There we go. Good job. That was a quick one. Right. I wonder if we... That's it for conditions fields. I wonder if we actually have time to go into... You can go either into the rich text field or into structured builder. Let's do the rich text field. I feel like that maybe... Maybe. You want to do structure builder? No, I think maybe rich text is more useful. I think so too. Yeah. At this point, at least. Right. So I guess post is the most obvious one, because what is a post if we don't have actual content? Yes. So rich text is a huge topic. And for over 20 years or so, we have done rich text in CMSs by having some kind of markdown or html editor going on. And if you have ever tried to move out of a wordpress site, you know that that can be kind of a hassle because plugins has inserted a lot of... I call it junk. A lot of markup and class names and a lot not into the content. And maybe people have copy pasted some stuff from Word. I guess wordpress maybe does a better job with that stuff now, but you can find a lot of things inside of that html that isn't helpful. So one of the core design principles when we built Sanity was like, we should never store html in your content backend. Not only because it makes it hard to break it, but especially now when you're doing react, if you get html through the api in react, either you have to parse it and break it down to make it malleable, or you have to use dangerously set inner html, and then you can lose control of what happens inside of it. And you have to set this classes aside and so on. It's a mess. So when you have something like react, you want to use your components and you want to have data as props into those components. And that also comes for rich text or block content because people not only want just formatted rich text, they want to be able to set in images and maybe a call to action, a newsletter sign up, like a YouTube embed, whatever. So in order to make this happen in a sensible way, we made a new specification for block content called portable text. I don't know what portable text is. It's kind of like a way to express block content, like what you have in Notion or Gutenberg, I guess, and so on. But embed that in JSON, an adjacent structure that you can query and so on. And that you can serialize in your front end and map different parts of your block content to different components in react. So what you don't want to do, you don't want editors to have to write JSON, right? So we needed to make an editor. And that's where I'm going with this, how to make that editor happen. And what's interesting is that block content is, again, the data type. It's an array of objects of some certain kind. So to make a rich text editor inside of Designated Studio, you say buddy, I guess. The type is action array, but it is an array of blocks. Let's see what happens. So if I say array of block, what you get is this rich text editor. It works pretty much as you expect. You can make things strong or emphasized and so on. And it also comes with this assumption that you want to make a link. So for editors, this is familiar. For you as a developer, let's look at the data structure this makes. So here is the content that I just made. It's a bunch of JSON. And this looks a bit daunting the first time you see it. How should I even wrap my head around this? It wasn't for me. If you take it bit by bit, it's kind of logical. Because what I have here is two blocks, I guess. It's two paragraphs. So here's the one paragraph and here's the other. A paragraph consists of children of spans. So here you can see the first span is hello, and it is marked up as a strong. And then we have world, which is marked up as like emphasis. And then we have the explanation point which isn't marked up. And in the second paragraph, we have kind of like, here's a link. The link has this reference to something called a mark definition, which holds our link data. And what's interesting with this is that this is kind of like, if you have a front end, we have a library that makes it more easy to map different parts of this text to components. So in your front end, you can say, you can kind of make a map that says, if you have a mark def called link, then you should insert this link component, use the href data for the actual link, I guess. I can maybe show that a bit later. But this also makes it possible to query for this data. So you can kind of like, now you can say, with Brock, you can say, give me all posts that has a link with Google as the href property and so on. Or give me all posts that has at least more than eight spans that are emphasized. So here you can start to see kind of like what database logic you can bring to search content once it's actually structured. Sometimes you want to kind of like restrict what options your editor should have here. Maybe it shouldn't be possible to use bold. And then you can start to customize this as well. So this I haven't actually done for a while. So if I go to blog content, customization, here you can see all kind of like the different properties. So I can say remove that, and only leave emphasis. And now it should only kind of like give me the oops. Yeah. I only have the emphasis button. And we can make, also we can make our own. And we can make, also we can make our own. We can, like it doesn't need to end up in html or like web page even. You can end up anywhere. You can say like highlights. Maybe you use this to kind of like print a book or something. I don't know. So you can also make your own just customized values. So now you can say highlight the text. No, this is highlighted. There's no like, we haven't inserted like how this should render. So it's kind of hidden. But if you look into the to the data, you can see that it's kind of like marked up with highlights. To make this blog content, we can just add more items to this array. So I can say type figure and save. And then we can also make this a bit larger. Then I have this plus button with the figure. And then I'm going to insert images. Like this. I can drag them around and so on. So that's pretty obvious like an image, but you can also make your own. So let's say we wanted to have a call to action component, for example, a CTA. That's an object. It has some fields. It has a title. Yes, or whatever you call it. And it has a URL. That's a pretty simple CTA, I guess. So now we have this CTA. Let's actually make this editor friendly. Call to action. I think maybe we can do this. I'm not sure what a useful emoji for call to action is. I think this works. Maybe I have to return it as a function. I think you do. It's kind of like a react thing. Yeah. Yeah, because otherwise we have to import. Yeah. So there we go. So you can use emojis and so on or like a react icon library to also customize these icons, which you should, because that's nice. So here we have... So this is a call to action block. And again, if you go into the data, we can see that we have... Here we have this neat structured content. And this will be kind of like a simple to map to a call to action component in your frontend. So if you are familiar with MDX, it's kind of the same principle, except you don't have to actually remember to write that component code inside of the content, but you get this data that you can map to props in your react components on the frontend. Yeah. But we also still, again, see the power of repeated patterns, right? We made that one figure object and now we've used it like four or five times in different ways. Exactly. I can see a question here. What's the difference between api CDN requests and api requests? So the difference is that api requests, they go kind of directly to the back end. So they aren't cached. That's the difference. CDN api is like a cached api that goes around where you have edge nodes around the whole globe. And api requests, they are going to direct it to the database, if you want. So when you want to make sure that your data is super fresh, like the studio is, like the studio wants the latest, freshest data, it will connect to the api. By the way, the studio usage aren't included. That's free. So that doesn't go towards your quota. But when you have a frontend application or a website, you usually want to hit the CDN. So when you make a query with graphql or Grok from your application, you usually want to hit the CDN. So you have that cached content and make sure that you get it from the closest server, the server closest to you. That's why we also find that there's a lot of more CDN requests included in the plans as well. Hopefully that explained it. Can we implement custom renders for the pipes? Yes, you can. I can show you a super simple example of that. Since we are at react advanced London, I assume you are familiar with react somewhat at least. So a simple, fast and loose way to do this is to import react. And then you can do some interesting things. I want to actually, it doesn't keep scrolled precision. I just want to cheat a bit and put this up there. I don't have to scroll. And then let's just delete this. So we have the command. Let's say I want to customize this with react. Then I can do a good thing. So again, it's preview, select, I can actually call this whatever. Then I can say component. And give it a component. I haven't actually made this component, so I have to make it as well. Props. Let's just call it print. data. Let's see if this works. Yeah, there we go. So here you can see that kind of like I took this preview and I replaced it with a custom react component. And I would just kind of like stringified the values and printed them out as kind of like this code. But I can also do like, I don't know. Does that actually work? Can I say? I'm not sure if this works, but. Yeah, I don't know. URL, I guess, targets, blank. I'm not sure if it actually works because of the, yeah, sorry. No, this is me. Because of the Hocus thing, maybe. Yeah, it did. So if you, for example, want to have kind of a YouTube embed thing, what could make sense is to kind of like actually just render the YouTube embed iframe inside of this, for example. But this kind of like shows you how you can start really making it your own. What's nice to know is that if you are venturing into this thing, we have something called Sanity UI. So the studio is built from this component library. So here you have all the primitives that you can reuse. So let's say I wanted more of a native feel for this, maybe card. Then I could do something like this. We got a mind blown emoji in the chat. It's pretty great. Yeah, it's pretty great. Then we can insert this and I can say import card text. That's the only one. That's right. From Sanity UI and this should probably just work. Hope. Yeah, right. Yeah, this comes in a way. But here you can see there's some padding going on. You get the gist. It's pretty great. I mean, we have in the Sanity studio, like our site, we have all sorts of stuff rendering inside of there. We've got code blocks. We've got some other stuff going on in there. Yeah. We also mentioned you can also make your own input components. It's a bit more work, but if you go to Sanity UI, for example, I think there's a tutorial for making your own custom input. We give you components that takes care of all the hard things. You can make custom inputs that supports presence, like that you can see that other people are in the field and validation and everything and wrap that around your custom input. Here you can find how you can use Sanity UI to make a custom string field. It's just a simple example. Let's say you wanted to make a string field that shows the character count underneath, then you could use this pattern to make that happen. You could repurpose that field across the whole studio if you wanted to. Hopefully you get the sense that yes, there's coding. It's not drag and drop and click, but it doesn't take a lot of time to get going, and get something that's useful and even start to customize it to whatever you need. Since I spent some time explaining Portable Text, I can also show you how, maybe a code example of how you render it in the front end. If we go into documentation again, hopefully there's some documentation on presenting Portable Text. There we go. Let's see if I can find. I know there's one for just react, just plain react, right? Yeah, here we go. This is a minimal example of both fetching data and making it happen. What happens here, this is the Sanity client. We need to modernize this. I see. Yeah. But it's pretty much the same today. Here we have the client, takes a project ID, takes the data set name. We have an api version. We follow the Stripe pattern. You can just put in the current date to get the latest version that's out. Then you can also choose if you want to hit the CDN or not. That's it. That's it to query all your published documents unless you have set the data set as private, but it tends to default to public. You don't need an api key and so on unless you want to access draft content. You should never put an api key in the front end anyways, but that's a side. So here we have the client. The client comes with a bunch of methods. One of them are fetch, where you can give it a grok query. Then it will return the article you asked for. Then we have something called block content to react. This is a library that's useful if you have portable text, if you use this block content field. Block content takes your blocks. This is the array. This is the body array that we have. Then it takes something called serializers. If you have your own custom types like we have, like the CTA type, then you need a serializer. Here we have a code example, but if I were to set this into our example, it will look something like, let's see, then I want to, oops, this toomchrom keeps getting in the way. Then I can say something like import react from react, oops, export, almost serializer. So types, that's called block types. That's the stuff we put in this array, so figure and CTA. So that means we need figure, CTA. And that takes kind of like the node. So inside of node, there's our data. So then we can say this CTA returned that as a div because we want to kind of like block out. And then gives an href node, href. Maybe I should look at the data just to kind of like keep you. No, yeah, there you go. It's not href, it's actually URL. And then take the title. There we have kind of like mapped this block to this relatively simple CTA code, right? And for the figure, oh, this is really challenging me, my front-end skills. Oh, yeah, thank you. We won't run this code, so. But the figure, that would be figure, I guess. Maybe there's an image tag. Let's say that we have the URL. Oops, we have the alt. And then we have, oh, yeah, I'm just messing this up. Then we have the good old feed caption, right? Load caption. And of course, in real world, you would make this more responsible. Responsible and responsive. Over appropriate. Yeah, so like we haven't touched on this, but like images, you have a bunch of stuff available to you. So to give you one example, whenever you upload an image to the content lake, we do an analysis of that image. So I can show you here. So let's go to, um, so here are the recent images. So we look at kind of like the file, we translate data into actual data. So you have kind of like an image palette. So you can kind of get a sense if this is a dark or light image, like if you want to superimpose text on it, you can kind of like use this information to control that text. And there's kind of like this. Let me see. Even like all the things you can do, it's like, because it gives you all the aspect ratio data. Yeah, I was just looking for the um, lkip and burhash stuff. But, um, any, any, we also kind of save a string that makes it possible for you to have this pre preload thing. Uh, any, anyways, uh, yeah, here we go. So there's a kind of like a low quality image placeholder. So you can use this in your css. Oh, um, yeah, it's, it's super small, but you can use this as kind of like before loading the image to have the smooth, smooth transition. Um, and you can also enable like XF data and geolocation and so on if you want to. Um, what was my point? Yeah. And also, if you want to make it responsive, you can also, um, let's say, let's have this image from our CDM. Right. So this is now, uh, 4608. Like it's, it's pretty high resolution. Let's say I wanted to have the, have 200 pixels wide. So I can just request that. So I can request whatever. Um, let's say I wanted this, uh, this is a JPEG. Um, I can also collect, make the asset pipeline return that P automatically if the browser supports it, but I can also say formats should be PNG. Uh, you can't see it. That is actually a PNG, but yeah, just have to believe me. Um, and so on. So there's a lot of parameters you can kind of like control here. Um, I can find everything inside of the documentation, of course. And there's also kind of like a tooling that makes it easy to, to get what you want. Yeah. There we go. So I just call it documentation that explains all the parameters. You can blur the image, you can crop it, you can, yeah, do a bunch of things. So that's useful to know. Right. Uh, I think we have a kind of like 10 minutes left. Yep. Do we have, uh, questions? Yeah. Like inclusive questions or whatever. I hope this was useful. I guess we have, have been so lucid and so clear that there aren't any questions. So, uh, all right. Um, so yeah, we have, this is kind of like the tip of the iceberg kind of situation. There's a lot more things you can do. Um, hopefully this has kind of like tempted you at least to try this out. If you do try this out, uh, do join our community, like developer community. Uh, we have a Slack, uh, where we have at least 9,000 people who, uh, who ask for help, who offer help, uh, the chair, kind of like things they've built and so on. So if you want inspiration, you can go there as well. People are looking for, um, they're looking for jobs and they are also posting jobs. Uh, there's a lot of things going on there and we have regular meetups and all that stuff. Um, so you are super welcome to join that if you want to. Um, yes. And, uh, sanity, we built sanity to, like, it should be, you should be able to spin up a sanity project whenever you, like, if you have a weekend project, if you just need some Jason in the cloud, you should just kind of run sent in it, make yourself a new project. It's, it's fine. Um, when you actually need content infrastructure for your company or whatever, you can do the same and should be able to scale with you. So, so yeah, so it's, it's not only for collect this huge projects. It can also be kind of like just to experiment with so long. I have hundreds of projects. Yeah. Uh, I have so many already and I've only been here for like eight months. Yeah. Um, so yeah, I think that's it.
168 min
28 Oct, 2021

Watch more workshops on topic

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