Optimising Developer Experience with Nuxt 3


Optimising developer experience with Nuxt 3 - a tour through the ways the new version of Nuxt will save time and make your life easier. We'll explore the new nitro-powered isomorphic fetch, dive into Nuxt 3's more powerful dynamic routing, and along the way tour a host of other features that will give you superpowers.


♪ ♪ ♪ ♪ Hi, I'm Daniel Rowe, and I'm going to be talking about optimizing developer experience with NUX3. developer experience is a topic close to my heart, and I think that's probably true for most of us here today. It matters a huge deal, doesn't it? The tools we use have the capacity to make our life painful or make it magical. At the moment, I'm speaking in my role as a member of the framework team for NUX3 itself, which means I get to maintain and work on the framework alongside a huge community of people who contribute and make NUX3 possible. In my previous role, though, I was CTO for a tech startup. Our main product was built on NUX3, and so I've really encountered NUX3 from both angles, both as a user being frustrated by issues, trying to solve them, and in fact, contributing and fixing issues was my first way of contributing to the NUX3 community. But I also, of course, have now got the perspective of working on the NUX3 team too, and really I'm going to be thinking about developer experience from both perspectives today. This is where I'm based. I'm in the northeast of the UK. It's already cold and crisp. It's definitely autumn, and this is where I sit most days. So I'll look across my desk and see my cat, one of my cats. I have three, but that's Lily in the picture, and I'll have a cup of coffee if it's in the morning or a cup of tea if it's the afternoon, and this is where I like to be. I like to sit and code or chat, figure out problems, and try and make things better. This is where I feel most in the zone. So I have a question for you. When have you felt most productive, most in the zone, most in that flow state that we sometimes talk about as developers? Just take a moment. Call it to mind. See if you can freeze that moment in your head. It might have been that you were confronting a problem, you had some great idea that you were implementing, you'd been on a bug hunt for, it seems like, ages, and you'd found it at last. Maybe you've published a library and you're seeing the GitHub stars come in, or you've just seen your first PR merge. Whatever the reason, it feels like you are on top of the world. So the more I've thought about my own moments of being in the zone or talk to others who've had the same experience, I think there are a lot of things that go into them, and it's a hugely complex topic. Obviously, we want to replicate it. It's addictive. It's a wonderful moment in time, and it depends on lots of things going on, everything from your own personal well-being to how much sleep you've had, how much caffeine you've ingested, to whether people are treating you well or fairly, whether they are giving you the value that you deserve, how you feel about yourself. But there are some common factors, I think, when it comes to thinking about the tooling that we use, which is particularly, of course, the topic that I'm thinking about today, ways in which the tooling we use can promote that in-the-zone moment, can promote our developer experience. And I think it's particularly around these two axes. So on the one hand, constraint, things that are holding us back, and on the other, creativity, the things that we're trying to do, that sense of vision or energy or impetus that we have. And now when I'm talking about constraint, I'm not talking about the constraints that you work with when you're trying to find a solution to a problem, like a business problem. I'm talking about the constraint that's more like red tape, that just holds you back, that prevents you from implementing the idea that you might have. Now, obviously, that might be hugely oversimplifying it, but I think you see great developer experience when you have that creativity, when you're going with the flow, you have some amazing idea, and there's very little, if anything, to prevent you from realizing it. So the more we make opportunities for creativity and the more we reduce constraint, I think we see better developer experience. And that is really where Nuxt has always sought to position itself. Don't worry if you don't know what Nuxt is. Nuxt is a view framework, a javascript framework. It's a progressive framework, which means that it's suitable for someone with no knowledge of it. You can pick up Nuxt, install a Nuxt project, and you should be able to start developing straight away. It doesn't require extra configuration. It works out of the box. But if you need to take ownership of it and take full control, you can do that. As you start building with Nuxt, you can configure pretty much anything. In fact, with the modules ecosystem and the whole modules container, you can configure everything about Nuxt and make it work almost in a completely different way from how it did when you first picked it up. It's a progressive framework, but it really is meant to take away a lot of the boilerplate. That zero configuration side of things is hugely important. It's in the Nuxt DNA. So when you pick up your Nuxt project, you might see there's a pages folder. Just drop any view component in there, and it will become a root into your app. It will be bundle split. It will be set up with view router. It will have a name. All the things that you might have to do previously as part of the boilerplate of a view project gets done for you automatically by Nuxt. More recently, you might have wanted to have a static website. Well, Nuxt makes server-side rendering possible. In fact, it was one of the few solutions that did. Early on in the days of view applications, it was quite a complicated thing to set up, and Nuxt made that possible. And more recently with static site generation, Nuxt makes that possible too. Lots of other things from vuex store integrations to automatic registration of middleware. I think probably the main thing that I'd highlight is this concept of modules, the idea if you need to implement a progressive web app or you need to implement authentication, you don't have to do it yourself. You have a CMS you want to integrate or some other service. You just pull in the module for it, and Nuxt makes it possible for you to get on with your idea without actually needing to step back and figure out the boilerplate you would require to properly set up that service or re-implement that concept. So no need to reinvent the wheel. The concept of Nuxt is really free you from constraint and free you to focus on that idea you have, your vision. Nuxt 3 really takes the same path again. So with Nuxt 3, there are lots of things to talk about, lots of ways in which we've tried to improve developer experience, but I want to focus on four today in particular. I want to focus on how we've sought to improve the documentation because that might sound minor, but I think it's quite a profound way in which we've sought to change the documentation, make Nuxt easier for devs to use. I want to focus on how we're auto-importing libraries, helper functions, and components. I want to think about our server developer experience because this is really possibly a completely different way of approaching server-side functionality. And I want to think about deployment and how we've sought to make the developer experience of taking an app and actually deploying it effortless and amazing. So first to dive into documentation. And when I'm talking about documentation, let me use the example of the Nuxt configuration schema. So the Nuxt configuration schema is an example of something that we need huge documentation for. If you go to the current Nuxt 2 docs, you'll see there are pages and pages and pages of information about how to configure Nuxt. Not that you have to do this, but when it comes to taking full control of your Nuxt application, there are lots of ways we provide to do that. And that's part of what it is to have a progressive framework. We have to give options because there's so much the framework does, so much heavy lifting it does for users. But the moment you have something that has a lot of configuration, it means that it's possible for the application itself and the documentation to diverge. So you can provide a new feature, but if you don't document it, it's no use to anyone. Or if the type or the option changes for that new feature and you don't document that properly, it makes a huge difference too. So with Nuxt 3, we have the opportunity to rethink this. And so we've come up with a single unified solution for schema, documentation, and defaults. So here's an example. This is a possible schema for a configuration item. This is the view config itself that will be used in a Nuxt app. And these are just two particular properties that I've pulled out, whether or not the app is in silent mode, which is typically what you want in production, and whether it has performance tracing involved, which is typically something you only want in development. So we have special resolvers that let us set what the default state for each of those is if there's no explicit configuration provided. And it depends on another entry in the config file, namely whether or not dev mode is enabled. But the thing that will really hopefully jump out at you is the fact that we have the docs there. So this is the documentation for our current Nuxt website, but we've put it directly in the schema itself. So what this means is when we're editing code or making a change, we'll have to change this file. And if we change this file, we have to change the documentation. That becomes part of making the PR to the core. Then when we actually compile the library up, we will have documentation and configuration that is unique, that is specific to that version of Nuxt. It produces something that looks a little bit like this in terms of a configuration schema file. All the information, but filled out a little bit more, pulling out everything from the JS doc types to the actual resolvers. And even producing a separate defaults file just to give you something you can interact with programmatically. And we have the utilities as well that enable you to consume a file like this. And the whole thing is released under a project called Untyped, which we are making public and can be used in any framework. So then when it comes to you actually interacting with your Nuxt app, say you're trying to configure it, it means that we're able to pull in the entire documentation from your Node modules folder where it lives into your editor. So as you're editing that config and you want to set one of the options, you can see all the docs that we have got. It's not huge in this particular instance. There would be some options that would have a lot more information, including code samples, links to other websites, and all sorts of other info. But it should give you an idea of the kind of functionality and the power of this ability. The fact that we're actually able to take the documentation and put it right there as you type means hopefully your experience as a dev is going to be dramatically improved. You're not going to need to search the online docs. You're not going to need to figure out even what the question is. Hopefully, the benefit of both the fact that everything is fully typed and fully documented as you go will make a huge difference. It's the kind of thing that would make all the difference for me. Second, we have this auto imports functionality. You'll be familiar with this if you've used the Nuxt components module, which can be enabled simply by setting components true in a Nuxt 2 project, but this is now built in in Nuxt 3. All the components that you put in your components directory become available for you to auto import on usage. If you are using that component in another file, in a page, for example, it gets bundled then to that page. Or you can say it's a lazy import, and then it will be generated as a separate chunk, maybe if you conditionally show it, something like that. We've taken it a step further. It's not just about components now, it's also about helper functions and libraries. We're really leaning into the benefits of having a framework here. Some of these kinds of things would be impossible if you weren't thinking of a framework, something that has control over both the bundler and the code transformer, but it is possible with Nuxt. This means that anywhere in your code base, you can actually use common functions like ref, watch, computed, or use router, or even Nuxt-specific composables like use meta. You just use them. Nuxt will notice that you've used them in that chunk, will automatically generate the imports that are required, and that means, of course, that we're able to do proper tree shaking at build time as well. When you use it, you have the import, and when you don't use it, you don't need to import anything at all. This is what that might look like, for example. If you're working in a component, you should be able to type. Now, define props, I should say, is just a compiler macro, but you should be able to use things like use meta, which enables you to have access to the metadata of a page, or ref, which is a view util, and with full typing. So it's not, say, specific to your memory or something like that. And that obviously flows through to the template as well. Third, we allow you some huge developer improvements on the server side of things. So the new Nuxt 3 server framework, and this is true as well for Nuxt 2 if you're using Bridge. In fact, a number of these features are common to Nuxt 2 if you're using Bridge. We built a new HTTP framework called H3. It's ultra minimal, so it's absolutely tiny. It's faster than a lot of the other frameworks out there. In fact, I'm not aware of a faster one. It's got lots of little utilities like use cookie or use body, use query that allows you to do things you might want to do for requests, but which are tree-shakeable, so they're not injected by default. It's cross-platform, so it runs in a browser as well as a service worker or a node environment and means that your Nuxt app doesn't have to be compiled to one particular target. It's not just expecting to run in a node environment. And it lets you do things like you can just return the javascript object from a network, from a request handler. You don't have to JSON stringify it up and set the content type header. Or you can just throw an error, and you don't have to worry about properly returning a response to that particular request. It's all handled for you. In addition, we have something called an isomorphic fetch. So an isomorphic fetch is basically a utility that lets you perform a local function call if it's being run on the server, but a proper network request if it's being run on the client. So your new Nuxt3 server, which is, by the way, generated just as a single file, rather than a directory of lots of files, doesn't have a runtime dependency on Nuxt. It's purely itself. It's all you need to run your app and run that server, but it doesn't have any runtime dependency on any of the Nuxt goodness that makes this possible. And this is a little bit of what it looks like. So the first thing that any request will encounter is this H3-powered orchestrator that will decide what bits of code are needed to render a response to that particular request. So if it's just one of the api endpoints, we won't load the whole view bundle. We won't load the view renderer or anything like that. We'll just load the code that is needed. If it is a page, for example, in your view app, that might be loaded up, and that page might have a dependency. It might want to make a request to one of the APIs that it's sharing space with in that server. Well, it doesn't make sense to float that request up to the HTTP layer and make it as a proper HTTP request. Not least, that's terribly inefficient in something like a serverless function. You really don't want to be hitting the endpoint again. The code is all there. It just needs to be loaded into memory. And so this fetch, this $fetch function, isomorphic fetch that we're talking about, tells H3, load this code up and give us the response. So it's an incredibly efficient way of getting a response from a serverless endpoint. And it just works. Not only does it just work, it's also fully typed. So you can create a serverless endpoint, say, time, and give us the timestamp of a particular request. And that's just going to return a number. We don't have to handle that number in any way. It's going to be properly passed by the isomorphic fetch. And we can actually just wait fetching that number. But the amazing thing is typescript actually knows the type of what that isomorphic fetch is actually going to return for us. That is pretty magical, if I do say so myself. And we might obviously have something a little bit more complex where we're not just returning a number, but we might need to access the default request and response objects in Node. Those are just typed, by the way, exactly the same as your normal request response in Node. Even though H3 might not be running in a Node environment, it will be marked out to have the same shape. So you would be able to access something like request URL or you can call res end if you really want to. Although a lot of the power of H3 is if you don't need to do that and you actually just directly return your JSON or just directly interact with params. So that would be totally possible. You could throw an error and have it automatically handled or you could access the request and response objects, and that should work perfectly. All of that means that it should be possible to dramatically improve your user experience with the Nuxt 3 server. And I think there are lots more enhancements that we are going to be able to offer in the coming weeks and months. So watch this space and certainly let me know if you have any ideas. Finally, deployment. And this is something that I think matters a huge amount because running a Node server is one thing. It has got lots of particular requirements that might be different from, say, running a PHP-based script or something like that. So for Nuxt 3, we've sought to make this as simple as possible. So first, as I mentioned, there's just a single entry point that you need for your Nuxt 3 app. So that could just be Node.output server.mjs. So this is what that might look like. For example, just build your app. It's incredibly fast, our new CLI. I've enabled timings on this particular one. So you'll see how long it takes to start, 3 milliseconds cold start. And I'll perform a request in another window. There you go. So you see how long it takes to actually load each bit of the request handler. Just milliseconds. So because we're able to generate just a single runtime, it means we're able to deploy to a vast number more targets than we ever would have been able to do before. The entire app can be bundled in 100 kilobytes rather than something like 5 meg or 10 meg, as you might previously have been dealing with if you were trying to deploy to a lambda function. But on top of that, a lot of the improvements that we've been able to bring to something like Nuxt Nitro have meant that we're now multi-platform. So we don't actually require a Node environment anymore, as long as the dependencies of your app don't require that. So you can deploy directly to cloudflare or to a browser service worker or to deno. More coming. The output of Nitro, the new Nuxt server, is capable of being deployed to almost any place you can imagine. And again, that's about removing constraint. You don't have to figure out how to make the Nuxt server that you get at the end of the build command fit into the deployment setup you have. It should just work. As of release date, Nuxt 3 has zero configure support for a number of platforms, particularly notable Netlify and Vercel, where it is absolutely no configure required. But that is true to an extent also with cloudflare, with azure functions, azure Static web apps. It's true with Firebase with very minimal configuration and more coming. Nitro enables incredible developer experience from the point of view of deployment. And really, we're looking forward to any feedback we've got on that. We want to make that as good as it possibly can be. If any of what I've been saying strikes a chord to you, please do let me know. We'd like to make Nuxt 3 as amazing as possible and backport as many of these features as possible to your Nuxt 2 apps, so you can actually benefit from all of this today, even before you migrate or build a new app with Nuxt 3. Check out the docs on nuxtjs.org. Note that some of them are auto-generated, as I promised. Follow us on Twitter and do join our Discord server if you'd like to, toss any ideas around. And then, of course, please do contact me directly because I would really value that hugely. It's been a real pleasure. And if there's anything I can do to help, don't hesitate to let me know. Thanks. ♪♪♪
26 min
20 Oct, 2021

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

Workshops on related topic