Remix is an amazing React framework that vastly simplifies the process of building fullstack React web apps. With this crash course, you’ll learn how Remix allows you to seamlessly blend frontend and backend code into each other. Besides crucial essentials like project setup, routing and styling you’ll also learn how to fetch and submit data, and how to integrate backend code into your React application.
👉 If you want to learn more about this awesome React framework, you can explore my full course “Remix - The Practical Guide”: https://acad.link/remix (a huge discount will be applied automatically). 👉 Learn more about React.js itself with my bestselling “React - The Complete Guide” course: https://acad.link/reactjs
Academind is your source for online education in the areas of web development, frontend web development, backend web development, programming, coding and data science! No matter if you are looking for a tutorial, a course, a crash course, an introduction, an online tutorial or any related video, we try our best to offer you the content you are looking for. Our topics include Angular, React, Vue, Html, CSS, JavaScript, TypeScript, Redux, Nuxt.js, RxJs, Bootstrap, Laravel, Node.js, Progressive Web Apps (PWA), Ionic, React Native, Regular Expressions (RegEx), Stencil, Power BI, Amazon Web Services (AWS), Firebase or other topics, make sure to have a look at this channel or at academind.com to find the learning resource of your choice!
Content
0.3 -> Remix is an amazing framework
2.28 -> that builds up on React,
3.99 -> that simplifies, vastly simplifies the process
7.62 -> of building full stack React applications
10.47 -> where you seamlessly blend frontend and backend code.
14.55 -> In this Remix Essentials crash course here,
17.16 -> you're going to get started with Remix
19.2 -> and all its core features.
20.79 -> And if you then wanna dive deeper,
22.74 -> if you also wanna learn about
24.15 -> adding authentication with Remix,
26.46 -> if you wanna build a complete demo project
28.89 -> and never demo project
30.39 -> besides the one we'll build in this crash course here.
33.21 -> And if you simply wanna learn more
35.31 -> about this amazing framework,
36.96 -> you also might wanna check out
38.34 -> my complete Remix course which I have,
40.8 -> Remix - The Practical Guide
42.48 -> to which you find a link with a great discount
44.97 -> below this video.
46.26 -> But with that, let's now dive back into discourse here
49.41 -> and let's take a closer look at Remix and what it is.
54.355 -> So what is Remix?
57.28 -> Well, it is a framework for React JS
60.3 -> and that of course brings up one important question.
64.29 -> Isn't React JS already a library?
68.22 -> Yes, it of course is.
69.75 -> React JS is a JavaScript library
72.69 -> for building user interfaces,
75.883 -> highly reactive user interfaces to be precise.
79.44 -> So why do we need Remix then?
82.5 -> Well, Remix builds up on top of React JS,
86.34 -> so you will still write React code,
89.19 -> but Remix adds a lot of extra features
92.31 -> that simplify the process of building
94.8 -> full stack applications with React.
98.7 -> Because if you're using just React,
101.31 -> if you have a normal React project
104.13 -> like I have it here in this frontend folder,
106.95 -> then you have a lot of code
109.708 -> that deals with displaying a user interface.
111.45 -> However, if that application also needs a backend
115.659 -> where you, for example, store data in a database,
118.02 -> you must add code to that React application
121.29 -> that sends requests to that backend,
124.69 -> to that separate backend server,
126.3 -> which then runs some server side code
128.46 -> that does actually talk to the database.
132.121 -> So in your frontend React application,
133.59 -> you have code for sending requests,
136.53 -> for managing some loading state
139.02 -> whilst the request is on its way so that you, for example,
142.47 -> can show some loading spinner or some loading display
147.18 -> and for handling any potential errors
149.64 -> that might be sent back.
151.08 -> That's what you do on the frontend.
154.02 -> And as mentioned, what you need then is a separate backend
158.994 -> if you wanna build a full stack application.
161.7 -> So that's a totally different project,
163.83 -> for example, built with note and express JS,
167.43 -> but you could build it with any programming language
170.82 -> and there you then do the actual work
173.4 -> of reaching out to a database or to a file
176.94 -> or wherever you are storing your data.
180.949 -> You could build such a backend yourself
182.16 -> or you're using some third party service like Firebase,
185.52 -> but either way you have a detached separate backend.
189.583 -> That's how you normally build full stack applications
191.94 -> when using React for the frontend.
195.477 -> And it's really important to understand and keep in mind
197.37 -> that in your fronted application
199.5 -> in the React app you're building,
201.72 -> you are not directly talking to some database
205.441 -> or anything like that.
206.274 -> Instead you are sending requests to the separate backend.
211.233 -> Now that request sending is often done with user fact,
214.296 -> which wraps the code for sending
216.069 -> such a fetch request, for example,
217.83 -> or if using the latest version of React router,
221.16 -> you could also use the new API offered by React router
224.97 -> for adding loaders and actions for sending requests
228.54 -> when a route becomes active, for example.
232.642 -> You still have a separate backend with that,
234.477 -> but the React code you have to write
236.407 -> gets much easier and leaner.
237.51 -> But I do cover React router in my full React course
240.72 -> and also in a separate video
243.447 -> which I created for my YouTube channel.
246.276 -> Either way, you have a detached backend.
247.47 -> Now if you are using Remix and stat,
249.78 -> and of course you'll learn how to create Remix projects
253.17 -> and which features Remix offers throughout this course,
256.8 -> but if you're using Remix, then these things change.
260.7 -> You still build a React application,
263.46 -> you still have a React component,
265.44 -> you still write React code,
267.27 -> but you blend your server side code
271.174 -> into the same file where you define your components.
274.17 -> So you have a mixture of frontend and backend code
277.23 -> in the same file.
278.64 -> The code here might look similar
280.38 -> to what you saw with React router
282.63 -> since I also have such a loader
285.075 -> and such a action function here.
285.99 -> But here we then actually have code
288.18 -> that runs on the server some backend code,
291.42 -> and therefore of course,
292.5 -> building a full stack React application becomes much easier
296.1 -> because you have one project
297.72 -> and backend and frontend code work together seamlessly.
301.29 -> And Remix this framework automatically splits your code
305.76 -> such that only the frontend code is served to your users
309.48 -> so they never see your backend code,
312.789 -> which could also contain sensible data, for example.
315.24 -> And on the other hand,
316.981 -> the backend code is executed on a server.
318.96 -> As a side effect that must not be underestimated,
322.56 -> the users of your website.
324.66 -> so the visitors of your website
326.67 -> also receive the finished website
328.86 -> when visiting your website.
330.6 -> So they get the finished HTML page
333.751 -> with all the data already injected into the page,
335.73 -> which also helps a lot with search engine optimization
339.855 -> since those search engine crawlers see the full website,
342.48 -> including all the data that makes up that website,
345.27 -> instead of just seeing the empty skeleton
348.18 -> that is yet to be populated by React,
351.966 -> by the client side code that is yet to execute
354.946 -> after the first version of the page has been loaded.
357.717 -> So that's a major advantage you get when using Remix
359.73 -> because Remix pre renders all these components
363.333 -> and all these pages on the server
364.38 -> and then serves the finished page to your end users.
367.44 -> So that's what Remix is and why you might want to use it.
371.19 -> It simplifies the process
372.9 -> of building full stack React application
376.105 -> by merging the code bases and it gives you many advantages.
379.08 -> And Remix has a couple of other amazing features.
381.75 -> And to be honest, writing Remix code and building Remix apps
385.71 -> simply is a lot of fun
388.215 -> because it's such an amazing framework.
390.6 -> Now in the last lecture,
391.77 -> you learned what Remix is and why you might wanna use it.
395.34 -> Now maybe what you learned there sounds familiar to you.
399.426 -> There is another framework for React called NextJS,
402.51 -> which kind of aims to solve the same problems,
405.63 -> which also helps with building full stack applications.
408.78 -> And indeed I do have a full course about NextJS as well.
413.1 -> But there are some important differences
415.11 -> between Remix and NextJS.
417.63 -> For example, with Remix,
419.28 -> you always render all pages on the server.
422.91 -> So whenever a user enters a URL leading to your page,
427.26 -> that page that's being served
429.691 -> is rendered on the server on the fly.
432.166 -> So the user always gets the latest data
434.506 -> and a brand new page.
435.339 -> Of course you can set up some caching if you want to,
438.505 -> but generally it is rendered when a user visits the site.
441.66 -> With NextJS, you can write some code
444.33 -> that forces a page component
445.89 -> to be rendered for every request.
447.9 -> But that's just optional because in most cases
451.05 -> you write code where the site is
453.42 -> statically generated at built time.
456.72 -> So right before deployment.
458.22 -> With Remix, this is not supported.
460.35 -> With NextJS, it's basically the standard
463.29 -> or the default for many pages.
466.38 -> Therefore for Remix, if you wanna deploy it,
468.66 -> you always need a host,
470.49 -> a server that supports server side code execution.
474.21 -> With NextJS, that's not necessarily the case
477.51 -> because there if you pre rendered all pages at build time,
480.96 -> you only need a static host.
482.85 -> You only need a host that's capable
484.8 -> of server side code execution for NextJS
487.74 -> if you are using this optional feature
490.457 -> of rendering certain pages on the server on the fly.
493.946 -> But these are just some technical differences.
495.69 -> There also are many syntax differences,
497.94 -> and even though I really like NextJS
500.43 -> and it is absolutely amazing,
502.56 -> I think that Remix has an even nicer API
505.838 -> and that that building Remix apps is even more fun
508.11 -> than it is to build NextJS apps,
510.18 -> which still is awesome, don't get me wrong,
513.267 -> but Remix is maybe more awesome.
515.28 -> We'll see after taking this course.
518.01 -> So now after learning what Remix is
521.4 -> and why we might want to use it,
523.11 -> and after understanding what this course is about,
525.99 -> it's really time to get started with Remix
529.358 -> and that's exactly what we'll do in this section now.
532.08 -> We'll dive into the crucial core fundamental Remix features
537.48 -> you must know to build any kind of Remix app.
541.32 -> And for that of course, we are first of all
544.256 -> going to create a new Remix application.
546.66 -> We're then going to explore the files that were created
549.72 -> and we're going to learn about a routing
552.42 -> and how we can configure different routes
555.75 -> and support different URL paths in our Remix app.
560.52 -> We're also, of course going to dive
562.5 -> into the super important topic
565.16 -> of data fetching and manipulation
566.73 -> since that's one of the main advantages of Remix,
570.45 -> if you wanna put it like that,
571.95 -> because that's what defines basically
574.35 -> any full stack application
576.48 -> that you store data in some data source
578.97 -> and you get data from some data source.
582.06 -> But we're not going to stop there
583.95 -> in this essentials section here.
586.77 -> We're also going to learn
588.06 -> how we can style our Remix application,
590.88 -> how we can make it look good, how we can handle metadata.
594.81 -> And after this section,
596.31 -> you will therefore have a solid understanding of
599.01 -> what it takes to build a Remix app
601.56 -> and how it generally works.
603.81 -> Thereafter in subsequent course sections,
606.84 -> we're of course going to dive even deeper into Remix,
610.17 -> explore these features in greater detail
612.51 -> and also dive into advanced features.
614.73 -> But for now, let's get started with Remix.
619.19 -> Now, to get started with building a Remix application,
622.08 -> we must create a new Remix project.
625.11 -> And we do that with a command we execute
628.492 -> in the command line in the terminal.
629.91 -> And it does not matter which operating system you're using.
633.24 -> The command is always the same,
634.77 -> and you always build Remix applications in the same way.
638.61 -> Now for this command which we're about to execute,
641.79 -> you must have note JS installed.
644.61 -> And in general, you must have note JS installed
648.09 -> because the Remix code you write
650.13 -> also includes note JS features.
653.01 -> Therefore, you should visit noteJS.org.
656.01 -> And there you can download this latest note JS version.
660.3 -> In my case it's version 19,
662.49 -> but you should simply download
663.81 -> the latest version you are seeing when you are viewing this.
667.649 -> And if that's a later version, that's perfectly fine.
669.09 -> What you'll learn in this course will still apply.
672.48 -> Now after installing note JS,
674.79 -> back in the terminal or command prompt,
677.753 -> you are ready to run the command
678.586 -> to create a new Remix project.
680.73 -> And that command is MPX,
682.83 -> which is a command made available by note JS,
686.52 -> create - Remix.
688.8 -> And then to be safe that you're using
691.29 -> the latest version of this Remix project creation tool
695.64 -> you should add @latest thereafter.
699.84 -> Now, make sure by the way that you are running this command
702.81 -> in a folder where you want to create your Remix project.
706.68 -> So you should navigate into such a folder in the terminal
710.16 -> in the command prompt before running that command.
713.7 -> Then simply hit enter,
715.29 -> and this will now take a short while until it gets started,
718.41 -> and then it will ask you a couple of questions.
721.14 -> For example, you must define the name of the project folder
724.68 -> that will hold the actual Remix project
728.219 -> that is about to be created.
729.641 -> And here, I'll name it a Remix - course,
732.135 -> and this will create a new folder called Remix - course
735.22 -> in the location where I'm currently at here in the terminal.
738.87 -> After hitting enter, we then can choose
741.3 -> whether we want a basic project or some preconfigured stack
746.16 -> with more features already added in,
748.86 -> and we'll stick to the basics here.
751.32 -> Then we have to choose where we wanna deploy this app,
754.62 -> though we can always change this later,
756.69 -> and I will come back to this question later in the course
759.66 -> when we do talk about deploying Remix applications.
763.71 -> To get started and for this course,
766.189 -> you should stick to Remix app server here.
770.04 -> Thereafter, you can choose whether you want to use
772.17 -> TypeScript or JavaScript.
773.94 -> And even though Remix is awesome with TypeScript,
776.7 -> I'll stick to JavaScript here since of course,
778.86 -> not every student watching the scores knows TypeScript.
782.25 -> And the general code you write is of course always the same
785.22 -> no matter if you're using JavaScript or TypeScript,
788.708 -> it's just the type annotations
790.714 -> that are missing with JavaScript.
792.99 -> So here, JavaScript it is.
794.46 -> And then I'll choose yes
796.432 -> to make sure that NPM install is random after.
799.862 -> And now this project is created
801.21 -> and all required dependencies are installed automatically.
805.946 -> Now let's wait for that to finish here.
808.86 -> And once the installation finished
810.66 -> and the project was therefore created,
813.648 -> you can open it with your favorite code editor.
815.91 -> In my case, that's Visual Studio Code,
818.948 -> but you can use any code editor you want.
822.114 -> And with that project created,
822.99 -> it's now time to take a closer look
824.85 -> at all these files and folders that were created
828.411 -> so that we understand what we got here.
830.67 -> So what do we have in this brand new Remix project?
835.756 -> Well, we got a bunch of configuration files
837.532 -> on the root level here.
839.248 -> A Remix configuration file
840.081 -> where we can set various Remix settings.
843.06 -> And I'll get back to some of these settings
845.28 -> later in the course
847.048 -> once we prepare our application for deployment.
849.66 -> Right now, the default settings are fine.
852.33 -> We then get the packaged adjacent file,
854.55 -> which contains a list of all the the dependencies
857.677 -> that are installed into this project.
859.23 -> Right now as you can see,
861.067 -> it's basically a bunch of Remix dependencies
863.25 -> and of course React,
864.87 -> since we're still building a React app here,
868.108 -> just a full stack React app of course.
871.362 -> And then we get various scripts which we can execute to,
873.48 -> for example, build this project for production
876.45 -> to start the production server
878.52 -> or very important for us for the majority of this course,
882.36 -> this dev script here,
884.01 -> which allows us to start a development server,
886.53 -> which allows us to preview the app whilst we're building it.
890.16 -> We then got a JS config file and a eslintrc config file
895.117 -> with further settings for the code editor we're using
897.6 -> and for how this project is being built.
900.09 -> But all the default settings are fine here for us
903.24 -> and therefore its now these folders
905.04 -> which are interesting to us.
906.93 -> Now, note modules as always
909.724 -> contains all the dependencies of this project
912.157 -> and you should not edit anything in that folder.
913.83 -> You can also delete this folder
915.63 -> and always bring it back by running NPM install of course.
919.59 -> This will then install all dependencies
921.57 -> listed in package.json and recreate note modules with it.
926.52 -> But the public and app folder,
929.687 -> these are the interesting folders for us.
931.47 -> Now, the public folder simply contains
933.51 -> any static assets your application might require.
937.237 -> For example, any images you wanna serve in your application
940.881 -> could be put into this public folder.
942.81 -> But it's the app folder that's most interesting
945.54 -> because you'll spend the vast majority of your time
948.27 -> in that folder.
949.92 -> Because in that folder you define
951.99 -> your React components you want to use and your routes,
955.68 -> your pages you wanna support in this full stack application
960.657 -> so that users can, for example,
962.488 -> visit mydomain.com/nothing and mydomain.com/news
968.159 -> or anything like that,
968.992 -> that will be defined here in this routes sub folder.
972.435 -> And that's something I will of course come back to
974.427 -> later in this section.
976.668 -> Now regarding the files we have here,
977.64 -> the route JSX file is a very important file.
980.85 -> This is basically the route component
984.42 -> that's always rendered no matter which route,
988.311 -> so which specific page is loaded thereafter.
990.66 -> Now I'll get back to some of these components
992.49 -> we have in this file a little bit later,
994.65 -> but essentially that's the skeleton,
996.6 -> the wrapper that's wrapped around any page content
1000.261 -> that you will define for your different pages
1002.991 -> that make up this application.
1004.863 -> You can also set up some general metadata here,
1006.71 -> though, as you will learn later in the course,
1009.424 -> you can also customize this metadata for different pages.
1012.35 -> But that's essentially the skeleton
1013.94 -> that will be used for any page
1015.59 -> that makes up your overall application,
1018.845 -> your overall full stack website here.
1021.62 -> We then got two entry files here,
1023.84 -> which essentially define the code that will be executed
1027.26 -> for every request that reaches the server.
1030.26 -> And you can edit this code
1032 -> if you wanna run some extra code for every incoming request.
1035.42 -> But unless you have clear plans what you wanna do there,
1038.87 -> you should stick to the default code you get out of the box
1042.44 -> and the entry client JSX file basically contains the code,
1046.64 -> the JavaScript code that's executed on the client.
1049.76 -> So in the browser after the page was loaded there.
1054.471 -> And here you should also basically keep the default,
1056.54 -> which you see here unless you know what you're doing.
1060.08 -> So these files aren't too interesting for us right now.
1063.2 -> I will get back to route JSX later,
1065.75 -> but for the moment,
1067.46 -> let's take a closer look at these routes.
1069.77 -> So what's the idea behind the routes folder
1072.64 -> in this app folder?
1073.473 -> Well, as mentioned before in there,
1075.53 -> we basically define the different routes,
1077.93 -> so the different pages.
1079.49 -> We wanna support as part of our overall web application
1083.03 -> of our overall website you could say.
1086.09 -> And we start with one single file in there right now,
1090.195 -> the index JSX file, which basically is our homepage,
1094.329 -> the starting page of this website.
1096.32 -> Now we get some dummy content in there
1098.21 -> and the exact content might change over time.
1101.81 -> So when you create a new Remix project,
1104.03 -> this content might be different.
1105.95 -> But that's not too important
1107.42 -> because I'm going to delete all this content here anyways
1110.66 -> and instead add a single H1 element and say hello world.
1116.24 -> Now please note that this here in the end
1118.46 -> is a regular React component function.
1121.64 -> It's a function that returns JSX code,
1125.079 -> that is the definition
1126.441 -> of a React component function essentially.
1128.69 -> It's exported as a default, and that's the important part.
1132.26 -> It's stored in a file in the routes folder.
1135.56 -> And any files you put into this routes folder
1138.26 -> will be treated as separate routes by Remix.
1142.1 -> So where with React router,
1144.17 -> if you're using that package in a regular React app,
1148.346 -> so in a non Remix app, you have to define your routes,
1151.94 -> for example, with Create Browser Router.
1155.09 -> You instead define Routes by creating separate files,
1158.48 -> one for every route in that routes folder in the app folder.
1163.355 -> Now index is a special file name
1164.51 -> which here defines the starting page
1166.57 -> of this overall website.
1168.92 -> But I could add a second file here, for example, demo JSX.
1174.71 -> And then in there export another default function
1178.55 -> called demo page, for example.
1181.767 -> The name of this component function does not matter,
1184.34 -> where I return H1 demo page.
1187.46 -> So where I also output a title
1190.191 -> that contains the text demo page.
1191.6 -> With that, I would've created a second route,
1194.42 -> a second page basically on this overall website,
1198.08 -> besides this starting page.
1200.81 -> Now to see this in action,
1202.52 -> we should open the terminal again.
1204.74 -> And here I'm using the one integrated
1207.269 -> into Visual Studio Code, but it's my default terminal still.
1210.59 -> And I'll run NPM run dev,
1212.84 -> and that's this dev server executed
1215.6 -> with help of this dev script, which I mentioned before.
1219.83 -> If we run this development server,
1222.41 -> we'll start up on the URL you see here,
1225.707 -> and if you visit this URL, you see your website.
1230.06 -> And out of the box, we of course end up
1232.798 -> on this starting page off this website.
1235.04 -> Therefore here if I zoom in a bit, we see Hello World.
1239.09 -> And we see Hello World because that's of course
1242.022 -> the content of this starting page route.
1245.09 -> Now if I do edit this URL though
1247.58 -> and I add slash demo thereafter, I end up on this demo page.
1252.844 -> I end up there because I entered demo here, slash demo.
1258.091 -> So demo is the path I added to my domain here.
1261.26 -> And since I named this file here, demo JSX,
1265.267 -> this loads this component here.
1267.956 -> Now, if I would enter any ever name here like ABC,
1270.38 -> I get an error, a 404 error to be precise
1274.07 -> because I haven't defined any file
1276.907 -> with the name ABC in my Routes folder.
1280.108 -> So these file names matter,
1280.941 -> they define for which path a certain route is loaded,
1284.866 -> and Index is a special file name
1285.77 -> representing the starting page here.
1289.172 -> But that's how routing generally works with Remix.
1293.51 -> Now, when it comes to routing and defining routes,
1296.96 -> Remix has way more to offer than what we saw here.
1300.53 -> It's not just about adding files that represent the paths
1304.52 -> which we can add after our domain,
1307.502 -> but this is one of the most important features.
1309.47 -> Now, for a start here, what I wanna do is I wanna make sure
1313.4 -> that we can switch between these routes,
1315.5 -> not just by manually entering different URLs here
1319.22 -> in the URL address bar of the browser,
1322.31 -> but by instead offering a link which I can click.
1325.37 -> And I'll start on this starting page.
1328.04 -> Here we could wrap this H1 element into a fragment
1331.61 -> with this special fragment component,
1334.958 -> which is a default React component offered by React,
1338.197 -> has nothing to do with Remix.
1340.168 -> And then below that we could add a link
1342.14 -> by adding a anchor element here
1344.75 -> and say go to demo page as a caption
1348.53 -> and then add the ref attribute, which points at slash demo.
1353.506 -> That's how we could add a link.
1354.712 -> Now if we do that, we can save that of course,
1358.55 -> go back to the starting page and we see the link here.
1362.21 -> If I click the link, it also works, I go to this demo page.
1366.71 -> But there is a small gotcha here.
1369.2 -> If you watch this refresh icon here,
1371.75 -> you'll see that as I click this link, watch this icon,
1375.68 -> it briefly changes to a cross,
1378.29 -> which indicates that a new request was sent
1381.8 -> and that we actually downloaded a new file.
1385.473 -> We can also confirm this behavior by open D developer tools
1390.2 -> and there going to the network tab.
1394.221 -> Here, if I click on go to demo page,
1398.42 -> we see that a bunch of requests were sent.
1400.76 -> Most importantly one request for the demo page, HTML file,
1405.645 -> which was received as a response.
1407.78 -> Now this has a couple of important implications.
1410.72 -> If we take a look at this response,
1412.37 -> we see that it indeed is a brand new HTML file
1415.61 -> that contains the content of this demo page.
1419.063 -> That's great for a search engine optimization, for example,
1421.73 -> because it means that we really downloaded
1423.68 -> a new finished HTML file.
1427.55 -> But the disadvantage of this
1429.38 -> is that we did send a new request.
1432.38 -> Now it would be nice if we're already on this page,
1435.35 -> if we could just navigate to a different page
1437.63 -> with client site JavaScript only.
1440.51 -> So if we enter a URL manually,
1443.644 -> it would be nice if we download the finished HTML code.
1446.27 -> But if we are on the website already,
1449.303 -> just on a different page and we then click a link,
1451.61 -> it would be great if the next page would be loaded
1455.303 -> by using the clients' JavaScript code,
1458.125 -> fetching any data that might be needed,
1459.02 -> maybe behind the scenes
1460.52 -> and otherwise not sending a new request
1464.377 -> because that saves us from downloading
1465.95 -> all the JavaScript code that makes up our application again.
1470.497 -> Because with Remix, you of course,
1471.5 -> still have client side JavaScript code,
1475.217 -> even though you are pre rendering pages
1477.537 -> on the server on the fly,
1479.502 -> but you still build a React application
1481.01 -> where you of course also still
1482.66 -> have client side JavaScript code, for example,
1486.561 -> to handle user input or React to button clicks
1490.297 -> or do whatever you wanna do.
1492.499 -> And to make sure that we're not leaving this page
1495.235 -> and we're re fetching everything when we click a link,
1497.63 -> we should not use this default anchor element,
1500.57 -> but instead a special component offered by Remix.
1504.89 -> And that component is imported from @Remix/React
1511.19 -> and it's the link component.
1515.36 -> Now this link component is a component
1517.43 -> you of course, might know from React router,
1521.235 -> which by the way is a package developed by the same team
1524.137 -> that built Remix.
1525.779 -> But here we're not importing it from React router,
1527.27 -> but instead from @Remix-run/React.
1531.47 -> Now, if we use this link instead of the anchor element
1535.85 -> and we replace the ref attribute with the to prop,
1540.32 -> then you'll see that if I save everything
1543.38 -> and I go back to the starting page,
1545.42 -> I still have the link here.
1547.43 -> But if you watch this refresh icon again as I click it,
1550.82 -> it does not turn to a cross.
1554.3 -> And if I open D developer tools,
1556.07 -> you will see that if I click this link,
1558.23 -> there is just one request for the favicon here,
1561.599 -> but no other requests being sent.
1563.54 -> So now we don't re-download everything,
1566.741 -> but we instead stay on the page on which we already were.
1569.48 -> So we get the best of both worlds,
1571.43 -> new visitors that enter a URL
1574.25 -> or click a link on some other website
1576.56 -> download the finished HTML files as they reach our website,
1580.97 -> but users that are already on the website stay on that site
1585.05 -> and basically get a single page application again
1587.72 -> after sending this first request
1591.098 -> when they first visited the website.
1594.442 -> So we added these two routes here.
1597.418 -> Of course they're not too exciting
1598.698 -> and therefore now I wanna switch to a more exciting project
1601.61 -> on which will work for the rest of this section here.
1605.48 -> This is what the finished project will look like.
1608.24 -> We'll be able to take notes, view the notes,
1611.06 -> navigate to the note details,
1613.34 -> and we'll also add a bunch of features
1616.362 -> which are not directly visible here in the end result.
1620.33 -> But in order to get there, back here in the project,
1623.882 -> I'll start by deleting this demo.jsx file
1627.96 -> and we'll also change the content of the index file here
1631.439 -> of this starting page, which we have here.
1634.538 -> For that attached you find a new index.jsx file
1637.88 -> which you should use to replace yours with.
1640.28 -> So your index JSX file should have this file content
1644.661 -> after replacing it.
1645.494 -> And as you see in there I got a couple of elements,
1648.44 -> some standard HTML elements supported by React.
1652.43 -> I got some IDs on some elements
1654.877 -> which will become important later for styling.
1656.57 -> At the moment they serve no purpose.
1659.6 -> And I got a link leading to slash notes
1662.81 -> and of course that's a route we're not currently supporting.
1666.35 -> There is no notes.jsx file,
1670.901 -> but I wanna have one because that will be the page
1673.25 -> where we will be able to add new notes
1676.9 -> and see a list of our notes.
1679.19 -> Therefore, as a next step,
1681.525 -> you should add a notes.jsx file in your routes folder
1685.397 -> to register this new route
1687.205 -> which will be reached whenever someone visits slash notes.
1690.961 -> So in our case, localhost 3000/notes.
1693.77 -> But of course, localhost 3000 will later be
1697.822 -> whichever domain you chose for your website.
1701.802 -> So that's the next file we add.
1704.485 -> Now in this notes.jsx file here,
1707.802 -> we should export a new function
1709.67 -> as the default for this file,
1711.62 -> and this function can be named notes page.
1715.497 -> Again, the function name isn't too important
1717.2 -> though it will appear in error or debug messages
1721.02 -> you might see if things go wrong.
1723.26 -> Now in this notes page component function,
1727.1 -> I wanna return some JSX code,
1728.524 -> because this should be a React component function here.
1730.78 -> Ad here to get started, I'll return the default main element
1734.78 -> and then in there H1 element
1737.943 -> where I simply say my notes.
1740.57 -> Now we'll refine this page later, we'll tweak it later,
1745.04 -> but for the moment that's what I'll output here.
1748.84 -> With that, if you save that
1750.7 -> and you start your development server again
1753.404 -> with NPM run dev, you will not find slash demo anymore,
1756.5 -> but you should be able to visit localhost 3000
1760.161 -> and see your starting page content.
1761.45 -> And keep in mind, I'm zoomed in quite a bit here,
1764.663 -> that's why it's that huge for me here.
1766.4 -> And then here I got this link
1768.29 -> which leads me to the notes page.
1770.27 -> Of course, it's super ugly right now, but we'll get there.
1773.57 -> This is an important first step.
1776.66 -> Now since it's quite ugly though, as a next step,
1779.54 -> I wanna add some styling to these pages.
1782.45 -> And how do we add styling in Remix apps?
1786.29 -> Well, you get various options.
1789.121 -> You can, for example,
1790.199 -> use popular CSS libraries like Tailwind,
1792.62 -> and you actually find a guide on using Tailwind with Remix
1796.91 -> on the official Remix documentation.
1800.21 -> In that documentation article
1802.07 -> to which you also find a link attached to this video,
1805.22 -> you also learn more about various styling options.
1809.244 -> But of course, I'll also go through
1810.524 -> the most important styling options together with you
1812.18 -> over the next minutes.
1814.25 -> And for this course, I'll stick to Vanilla CSS,
1818.7 -> though no worries, you don't have to write the CSS code.
1821.18 -> I will provide it to you.
1823.921 -> But I'm doing this so that we don't have
1825.819 -> some extra dependency on some totally independent
1828.2 -> and not Remix related CSS library.
1832.364 -> Now to get started, attached to this lecture,
1833.78 -> you find a main.CSS file which contains some styling
1838.14 -> I wanna apply to this page.
1839.51 -> But where should this file be stored?
1842.06 -> Well, it generally is up to you,
1844.79 -> but I prefer to create a new folder in the app folder
1847.58 -> which I name styles.
1849.2 -> Though unlike routes, styles is not a reserve name.
1853.34 -> The routes folder must be named routes
1855.8 -> because Remix will look for route files
1858.864 -> in that routes folder.
1859.97 -> But the Styles folder does not have to be named styles
1864.059 -> because we will manually point
1865.1 -> at the style files we add there.
1867.35 -> So therefore you can put the main.CSS file
1871.44 -> which you find attached to this lecture
1872.45 -> into this styles folder so that it's in there.
1875.941 -> Now, in that main.CSS file, I'm defining some CSS variables,
1880.52 -> some colors which I'm basically using,
1882.71 -> and then I'm setting up some fonts, some background colors
1885.95 -> and some styles for example, for domain and navigation,
1889.584 -> which we'll add soon.
1890.72 -> So some styles which will be used throughout this section.
1894.62 -> And if you add this file and you reload, nothing changes.
1900.05 -> Well, because as I just mentioned,
1902.06 -> Remix does not automatically look for such a styles folder.
1906.58 -> It doesn't expect a styles folder.
1908.901 -> This name is up to you.
1910.222 -> If you have some styles that should be applied to your page,
1912.17 -> you have to tell Remix about it.
1915.563 -> And you do tell Remix about styles you wanna apply
1918.72 -> by adding special functions to your route files.
1921.05 -> Here main CSS actually contains some styles
1924.17 -> that will affect all my pages
1926.36 -> and therefore instead of adding it
1927.77 -> to all my route files separately,
1930.901 -> which would be a lot of redundant work,
1932.06 -> I'll add it to this route.jsx file.
1935.24 -> I mentioned before that a route JSX
1938.09 -> defines basically the main skeleton
1941.082 -> that will be wrapped around all your page components.
1943.82 -> To be precise, your page components,
1946.31 -> like this markup returned by notes page
1949.61 -> will be injected in this outlet place here.
1953.12 -> So this outlet marker here in this root skeleton
1958.16 -> will be replaced with the actual content
1960.26 -> returned by your page components.
1963.28 -> That's how that works.
1964.91 -> So this is the root page, the root component
1969.058 -> that wraps all my other page components
1970.46 -> and therefore in this route JSX file,
1972.95 -> we can export a special function
1975.41 -> and we must export a special function.
1978.901 -> And that function should be called links.
1981.08 -> Now that's a reserved name.
1982.79 -> Remix will be looking for a exported function called links.
1987.38 -> It looks for such a function in all your route files
1990.8 -> and the route file, the route.jsx file
1993.963 -> counts as a route file.
1995.21 -> This is your route route that wraps all other routes,
1999.323 -> so to say.
2000.156 -> Now you don't have to export a links function,
2002.65 -> Remix works without it as well as you saw before.
2005.77 -> But you do need this function
2007.57 -> if you want to add some extra links
2010.901 -> that should be added in the head section
2013.003 -> of your HTML document.
2014.8 -> Because you might notice that this skeleton
2017.41 -> does not justify the marker
2019.54 -> where the actual page content will be injected,
2022.45 -> but also some other components.
2024.91 -> These are some utility components
2027.541 -> for getting life reload during development,
2029.65 -> for restoring the scroll bar position
2032.95 -> if users navigate between pages
2035.65 -> and for injecting the client side scripts
2038.53 -> when the page is downloaded on the client side.
2042.34 -> But we also got these components in the head section,
2045.04 -> which are used by Remix to inject any metadata
2048.13 -> or any links that apply to pages.
2051.4 -> And with links, I mean link elements like this one,
2055.03 -> that's how we could point at a style sheet
2058.501 -> in a regular HTML document.
2060.22 -> And instead of adding link here,
2062.11 -> we add our links with that links function.
2065.62 -> This function, if you add it, should return an array.
2069.73 -> And in that array you have various objects
2072.61 -> where every object defines one link
2074.95 -> that will be injected in this links place here by Remix
2078.43 -> when it renders your page.
2081.04 -> Here you add all the attributes
2082.93 -> you would add on this link element
2085.99 -> as properties in this object.
2088.51 -> So here I add the rail property and set it to style sheet
2093.19 -> as I would've added the rail attribute
2096.16 -> with a value of style sheet
2097.93 -> to the link element I could have added here.
2103.434 -> And then I add another property called ref,
2106.69 -> which points at the style sheet that should be loaded.
2110.741 -> And now here's a special twist.
2112 -> You do actually import your style sheets
2114.58 -> into your JavaScript files like this.
2118.101 -> You add a new import statement in your file
2119.41 -> where you wanna import it.
2121.33 -> Then you give it any name of your choice like styles,
2124.45 -> and then you point at the file that should be imported.
2128.05 -> Here we can point at slash styles,
2131.28 -> ./styles to be precise to create a relative path,
2134.59 -> and then main.css.
2137.83 -> By the way, you can also use the tilde symbol here.
2140.89 -> That's a special alias
2143.243 -> which you can use in your import statements
2144.43 -> and it will always refer to your app folder.
2147.984 -> So this is looking for a styles folder in your app folder,
2151.04 -> which is a folder that exists.
2153.22 -> So this will import my styles, and now it's these styles
2157.301 -> which we use as a value for ref here.
2159.76 -> This dynamically creates a link to this style file,
2162.28 -> which can be loaded by the browser.
2164.92 -> And that's how you should register your links.
2167.723 -> Now this might look rather cumbersome
2170.23 -> compared to simply adding a link here,
2173.92 -> but we do this because as I mentioned before,
2176.5 -> all your page components can register links
2180.701 -> and can define links, page specific links.
2183.46 -> Now here we have one in the route page
2186.301 -> that should apply to all pages,
2187.33 -> which is why we are adding it here.
2189.37 -> But we could also export links in index JSX or notes.jsx.
2193.72 -> And indeed that's what I'll do.
2196.03 -> I add another CSS file, which you also find attached,
2199.63 -> home.CSS to my styles folder.
2202.78 -> And this file contains some styles
2205.06 -> that apply only to the content of the index JSX file.
2209.38 -> The IDs content and CTA can be found here
2212.56 -> for styling purposes in home.css.
2216.179 -> Now in this index JSX file in the routes folder,
2220.78 -> we can also export this links function.
2224.86 -> And there we also return an array
2227.35 -> where we have an object with a property rail
2229.93 -> with a value of stylesheet,
2232.36 -> and then another property called ref.
2235.27 -> And we then import the home styles
2238.69 -> or however we wanna call this from ~/styles/home.css.
2246.13 -> And then it's my home styles which are registered here.
2250.838 -> Now we're defining some links that only apply to this page,
2254.74 -> and again, Remix will automatically create the link tag
2257.89 -> and inject them in the head section
2260.73 -> when this page is loaded.
2262.54 -> Now we have this page specific approach here
2265.99 -> where you can define page specific links
2268.81 -> so that users don't unnecessarily download CSS files
2272.68 -> if they don't need them.
2274.835 -> If a user is on the notes page,
2275.71 -> they don't need the styles for the starting page.
2279.13 -> That's why you can define your links
2281.8 -> to download style files, for example, on a per page level.
2287.05 -> Well, and with these adjustments made,
2289.757 -> you already see that we got a different look here.
2292.24 -> Now of course you can tweak it,
2294.493 -> it's up to you whether you like that look,
2296.435 -> but we got a new look.
2297.52 -> If I click on try now we see that for the notes page,
2300.55 -> we got this general background,
2302.62 -> which is defined here in main CSS,
2305.35 -> which is loaded for all pages since we exported in route,
2309.37 -> but we got no page specific looks here.
2312.477 -> That's only the case here for the starting page,
2315.46 -> and that's how we can generally style our pages.
2319.09 -> Now when it comes to styling,
2320.47 -> there's one last aspect of styling which I wanna show you
2325.155 -> and which I wanna talk about.
2326.5 -> Let's say on this notes page,
2328.78 -> we finally want to add more meaningful content.
2332.23 -> For that attached, you find a React component,
2335.17 -> a component which will not use as a route, as a page,
2338.86 -> but as a regular component that we use
2341.816 -> as part of other components.
2343.18 -> To be precise, the attached component should be used here
2346.15 -> in this notes page component.
2349.06 -> For that attached, you find a new note.jsx file
2353.795 -> and a new note.CSS file.
2356.26 -> Now I'll add a new folder in my app folder
2359.513 -> called components, though this name is all up to you.
2361.87 -> It doesn't have to be components,
2364.573 -> it could be any name of your choice,
2366.271 -> Remix will not be looking for it.
2368.555 -> But it makes sense to name it components
2369.73 -> because I'll store all my non page components,
2373.334 -> all my non page React components in there.
2375.91 -> So the attached new note.jsx file and CSS files go in there.
2380.77 -> Now if you take a look at this new note component file,
2384.274 -> you see that in there we got a form.
2385.997 -> This form will become important later for adding new notes.
2388.84 -> At the moment, we can ignore the content
2391.357 -> and as you see, it's also not too exciting.
2394.056 -> It's really just that form with some inputs.
2396.477 -> And then we got some styles for this form and D inputs.
2399.64 -> Now I wanna use this new note component
2401.83 -> in this notes.jsx file.
2404.35 -> And there it should be used instead of this H1 element.
2408.4 -> To do that, we first of all have to import
2411.34 -> this newly added component into this notes.jsx file.
2415.36 -> So we import new note from,
2418.25 -> and then we can use the tilde symbol
2420.093 -> to get our quick access to the app folder.
2422.68 -> And then access/components/new note.
2425.89 -> You can omit the file extension.
2428.56 -> And then here we can use new note as a component.
2432.856 -> like we do it in any other React app as well.
2435.53 -> There's nothing Remix specific about this code here.
2438.41 -> It's a standard React component
2440.253 -> using another React component, which is imported.
2443.77 -> Now if you save that,
2445.18 -> you'll notice that the form is visible,
2447.896 -> but of course not styled, not looking good.
2451.42 -> Now to apply that styling here,
2454.173 -> we have to expose this style file while links.
2456.82 -> Now for doing that, we get a couple of options.
2459.76 -> We can go to our notes.jsx file to this route file.
2464.32 -> And for example, not just import the new note component
2468.055 -> from the new note.jsx file,
2469.63 -> but also import the new note styles
2471.7 -> from /components/newnote.css.
2477.194 -> And here you need the file extension.
2479.379 -> You do need it for .CSS files.
2481.416 -> You can only omit it for JavaScript and JSX files.
2485.135 -> Now with that imported, we can of course,
2488.397 -> export this links function as we did before,
2491.11 -> return that array
2492.76 -> and make our style sheet available here like this.
2498.04 -> That's what we did before.
2500.5 -> And if we do that, we got the styling applied.
2504.84 -> So that works and that is a perfectly fine way of doing it.
2507.619 -> Now there is an alternative approach though,
2511.32 -> so that you don't have to add this extra CSS import here
2515.059 -> in your route component file.
2517.39 -> We could go to the new note.jsx file
2520 -> and we could import this styles file from here.
2525.416 -> Now of course, we still import
2526.249 -> the new note.CSS file in the end,
2528.92 -> but it might make more sense to do it here
2531.176 -> in the new note.jsx file, which sets next to the CSS file.
2536.277 -> With those styles imported here,
2537.61 -> we could then export them from this new note.jsx file.
2543.117 -> To be precise, we could export
2544.313 -> a links function here as well.
2547.21 -> And that function has the same shape
2549.778 -> as it does in my route files.
2552.198 -> So I add rel stylesheet and then ref styles.
2559.517 -> Styles is the name I chose here for the import.
2562.717 -> So I added this links function to this component.
2565.096 -> Now the problem is
2567.296 -> Remix will not be looking for links functions
2570.01 -> or any other route component specific functions
2572.71 -> in files that are not route files.
2575.14 -> And since this is a regular component file
2577.18 -> not in the route folder,
2578.74 -> Remix will ignore this links function.
2581.92 -> But of course, we can use it.
2584.05 -> In this note.jsx file,
2586.27 -> we can add more than just the new note component function
2589.973 -> from the new note.jsx file.
2592.293 -> We can also import this links function which we export.
2595.03 -> And we could give it an alias, maybe new note links.
2599.376 -> And now here in the links which we return for the notes page
2603.61 -> we could simply merge in all links of any components
2609.513 -> that we might be using.
2611.92 -> So we execute new note links here,
2614.933 -> this function which we're importing from new note,
2616.99 -> and this function returns in array.
2619.66 -> And then I use the spread operator
2622.174 -> to merge the array items into dis array
2624.494 -> returned by dis links function
2626.193 -> in this route component file.
2627.936 -> This is a pattern called surfacing links
2630.773 -> and it is actually a pattern
2632.395 -> that's also mentioned and explained
2633.755 -> in the official Remix documentation,
2635.26 -> which is why I'm also mentioning it here.
2637.51 -> It is some extra work, but it makes sure that you can use
2640 -> one consistent API for making your style files available.
2644.41 -> One thing I wanna mention though, which is really important
2647.53 -> is that those styles defined in this new note CSS file
2651.43 -> will not be scoped to this component.
2654.28 -> They are still global styles.
2656.86 -> If you are defining some clauses in here
2658.93 -> like form actions, which I am,
2660.97 -> and you're using that clause in some other component
2663.64 -> that's part of that same page as well,
2666.16 -> those styles would be applied
2668.471 -> and would potentially clash with other styles.
2670.57 -> We're not using CSS modules here.
2673.671 -> We got no scope styles.
2675.04 -> So we have some component specific styles kind of,
2678.34 -> but they're technically still global.
2680.53 -> It still makes sense to manage them in a separate CSS file,
2684.1 -> which might be kept close to the JSX file
2686.71 -> so that maintaining this component and it styles
2689.5 -> is as easy as possible.
2692.448 -> Ultimately it is of course up to you
2694.477 -> which approach you prefer.
2695.852 -> If you want a surface styles like this,
2697.18 -> if you wanna put everything into one global style file,
2699.94 -> which is stored in the styles folder
2702.509 -> and maybe then import it into route JSX,
2704.8 -> or if you wanna import style files
2707.32 -> into your route page components
2709.949 -> and then use this links function, it is up to you.
2712.77 -> I'll stick to this surfacing approach here,
2715.487 -> but I wanted to show you these different approaches
2716.89 -> so that you can choose your personal favorite.
2720.13 -> Now with styling out of the way,
2722.29 -> before we dive deeper into more Remix features
2725.08 -> and we especially dive into data fetching and mutation,
2728.47 -> I wanna add one other new component to this page,
2731.47 -> and that's a main navigation bar component.
2734.62 -> For this attached, you find a mainnavigation.jsx file
2738.853 -> which you should store in this component's folder
2740.74 -> because this again is not a standalone route component,
2744.01 -> but it instead is a component we'll use
2747.071 -> instead of other components.
2748.65 -> And if you take a look at the code of this component,
2750.1 -> you'll notice one new component
2752.629 -> which you might have not seen before,
2754.25 -> the NavLink component,
2755.749 -> which is imported from Remix run React.
2758.14 -> I'm using it basically like the normal link component
2761.92 -> which you saw and used before.
2764.35 -> But NavLink has one special feature.
2767.11 -> The link that's currently active
2769.09 -> receives an active CSS clause,
2772.42 -> and that clause can be used
2774.747 -> for giving that link a special style.
2776.47 -> And indeed in main CSS, which we added early already,
2779.47 -> I got some main navigation styles
2781.48 -> and also a special style for anchor text
2784.03 -> in the main navigation
2786.191 -> that do have this special active clause.
2788.62 -> This simply makes it easier for users to see
2791.53 -> which link in the main navigation
2794.049 -> led them to the currently active page.
2797.17 -> Now we just have to use this main navigation component.
2800.83 -> And I of course wanna make sure
2803.489 -> that it's visible on every page of our website.
2806.35 -> For that, we could of course go to our different routes
2809.5 -> and add this main navigation component
2813.29 -> in every page component,
2815.8 -> but that would be rather cumbersome.
2818.591 -> Here we of course only have two routes, only two pages,
2821.83 -> but in bigger applications you easily have dozens
2825.253 -> or maybe even hundreds of pages.
2827.551 -> And you don't wanna start adding shared components manually
2829.72 -> to all these pages.
2831.82 -> Instead it's again the root component
2833.59 -> that we can leverage here.
2835.988 -> Because as mentioned before,
2837.13 -> this skeleton is wrapped around all our pages
2839.68 -> and therefore it's the perfect place for shared components
2843.151 -> like this main navigation.
2845.02 -> Hence here in my body I'll add a header,
2848.95 -> the standard header element,
2850.72 -> and then add my main navigation here.
2854.62 -> For that, we just need to import this component
2857.23 -> and therefore here at the top of this file
2859.36 -> I'll import main navigation from tilde slash components.
2865.24 -> And then MairMainNavigation.
2869.279 -> That makes this component available in the route JSX file.
2874.053 -> And I'm then using it like this here in my body
2877.071 -> above the outlet, so above the place
2879.13 -> where the page component content will be injected.
2882.85 -> With that, if we save this,
2884.41 -> we get this navigation bar at the top here
2887.429 -> and you see that the active link is highlighted
2889.81 -> because it does receive this special active CSS clause,
2893.98 -> which is applied here because we're using NavLink
2897.01 -> instead of link.
2899.02 -> With the main navigation added
2901.06 -> and our website finally taking some shape here,
2904.39 -> it's time to make sure that
2906.709 -> we can actually create notes here and submit those notes,
2911.26 -> that we do something on the backend
2914.314 -> because we're all here
2915.311 -> to build full stack applications in the end.
2916.87 -> Because I mentioned that
2918.613 -> this is what Remix would be all about
2919.45 -> and right now there's nothing
2920.86 -> full stack-ish about this application
2923.05 -> as we have absolutely no server side code in there.
2927.594 -> We just have some React components
2929.114 -> and we're doing some routing,
2930.394 -> which we could be doing on the client side
2931.994 -> with React router as well.
2934 -> Well now we're going to change things.
2936.58 -> In the new note.jsx files, we already got a form
2939.67 -> which will be submitted when this button is clicked.
2942.37 -> And if you've worked with just React in the past,
2946.9 -> you often added a submit handler here on submit.
2952.06 -> You added a handler like this, submit handler
2955.45 -> and you pointed at this handler here.
2958.36 -> And then you got an event here
2960.597 -> and you typically called event.PreventDefault.
2965.29 -> Now if you have used React router version 6.4 or higher,
2969.07 -> chances are you've not been doing this
2971.38 -> because with that version of React router,
2973.6 -> some new APIs were introduced
2975.76 -> that actually let you handle form submissions
2978.04 -> in a more elegant way.
2980.538 -> And I do have a separate video about that
2981.76 -> and I do of course also cover this
2984.378 -> in my complete guide React course.
2986.68 -> But here we're not going to use React router
2990.335 -> because we're using Remix
2991.557 -> and we're not going to add a submit handler like this.
2993.25 -> We're not going to prevent the default.
2995.38 -> Instead with Remix, we're going to embrace the default.
3000.12 -> And how do we do that?
3002.13 -> Well, this form has a method
3003.99 -> and we're therefore going to send a post request
3007.759 -> whenever this form is submitted.
3008.79 -> Now if you have worked with HTML form elements before,
3012.12 -> you might know that you can also add
3014.571 -> the action attribute here.
3016.53 -> This enables you to define a path
3018.96 -> to which this post request should be sent
3021.54 -> when this form is submitted.
3024.277 -> And by the way, the form will be submitted
3025.17 -> whenever this button is clicked.
3027.914 -> A default button in a form submits the form.
3030.09 -> Here you can define the path
3031.98 -> the post request should be sent to,
3034.26 -> and we could use notes here,
3037.02 -> to send it to this notes page here to this notes route.
3040.56 -> And soon we'll add some code here
3043.183 -> that actually handles the form submission.
3045.75 -> But we actually don't have to add the action here
3048.42 -> because this new note component
3050.34 -> is already used by this page component
3053.64 -> defined for this notes route.
3056.458 -> We're using new note here
3057.72 -> and therefore this new note form
3060.357 -> will actually be loaded for slash notes.
3062.04 -> So for this notes route.
3064.778 -> And if you then submit a form,
3066.296 -> it will by default send a request
3067.41 -> to the path that's currently active.
3070.02 -> So two slash notes if this form is rendered
3073.855 -> on the slash notes route, which will be the case
3076.575 -> since we used that new note component in this notes page.
3079.607 -> So therefore here, this form
3083.053 -> would by default create a request
3083.94 -> that's sent to slash notes.
3085.92 -> And we can see this in action.
3087.66 -> If we go back to our page here
3089.97 -> and we opened the developer tools in the network tab
3093.15 -> and we enter some content here and click add note,
3096.87 -> it will crash right now.
3099.018 -> But what you'll see is that here in the network tab
3102.03 -> it actually tried to send a request to notes
3106.47 -> and it was a post request sent to localhost 3000/notes.
3112.698 -> Now it failed because currently
3114.66 -> we have no code handling this post request,
3117.45 -> but it did do the right thing,
3120.138 -> it tried to send that request.
3121.957 -> So therefore now we just have to add
3124.74 -> some code to this notes route
3126.378 -> to handle that request, which currently fails.
3129.301 -> And that's exactly where we'll start building
3131.418 -> a full stack application now.
3133.68 -> Because now we have to add a new function here
3136.62 -> to this notes.jsx file and we have to export it.
3141.178 -> And this function must be called action.
3143.25 -> This is a name Remix will be looking for
3146.277 -> just as it was looking for links.
3147.84 -> And indeed there are a couple of special functions
3151.237 -> which you can export in your route files.
3153.33 -> Remix will be looking for and action is one of them.
3156.96 -> And you might notice action function
3158.79 -> if you have worked with React router version 6.4 or higher.
3163.44 -> But this action here in this Remix app will work differently
3167.91 -> because with React router we had some client side code
3171.87 -> that would be executed upon form submissions.
3175.194 -> Now we'll have some backend code.
3176.94 -> Whatever we put into this action function
3179.13 -> will run on the backend, on the server, not in the browser.
3183.6 -> And the codes defined in this action function
3186.917 -> therefore will also not be downloaded to the client.
3190.298 -> Remix will split the code
3192.098 -> and only execute and store that code in this action function
3195 -> on the server.
3197.61 -> But when exactly is this action function triggered?
3200.94 -> Well, it is triggered
3202.44 -> whenever a non get request reaches this route.
3206.22 -> If a get request reaches slash notes,
3209.52 -> it's simply the component that's returned,
3212.715 -> so this page component.
3214.476 -> And a get request is for example sent
3215.73 -> if we just enter slash notes and we enter here,
3218.46 -> or if we click a link that leads to slash notes.
3222 -> If a non get request reaches this route like a post request,
3225.48 -> which is what we're sending here from this form,
3227.82 -> then it's actually the action function
3230.959 -> that will be triggered instead of the component function.
3233.55 -> And here in this action function,
3235.47 -> we can then execute our server side code
3237.96 -> that extracts the submitted data
3240.18 -> and maybe stores it in a database
3243.098 -> or in a file or whatever you want do.
3244.8 -> For data attached, you find a notes.JS file
3248.43 -> and in the app folder you should add a new data folder.
3251.82 -> This folder name again is totally up to you
3255.117 -> and not reserved, but I'll put my notes.JS file,
3258.842 -> which is attached to this lecture into this data folder.
3261.84 -> Now in this notes.JS file, I just got some utility functions
3265.59 -> for reading data from a file
3268.35 -> and for storing data in a file
3271.743 -> in a notes.json file, to be precise.
3275.16 -> Now we could be interacting with a database here
3277.71 -> and in most apps you probably will,
3280.634 -> but to keep things simple here,
3282.39 -> we'll just use this file as a storage
3285.759 -> because it doesn't really matter what we use here,
3287.792 -> the Remix code is not affected by that.
3291.381 -> Now we just have to add a notes.json file
3293.55 -> here on the root level of this project,
3296.697 -> so next to the package.json file.
3299.397 -> And this notes.json file will receive an empty object
3303.301 -> if you want to call it like this, where I add notes,
3305.79 -> which is an empty array initially.
3310.197 -> Now this is the json file format here,
3311.52 -> and we should have this initial structure in notes.json.
3317.759 -> Now with that added back in note.jsx,
3321.48 -> we can interact with this note.json file
3325.496 -> with help of these utility methods stored
3327.919 -> in note.JS in the data folder.
3330.682 -> And that is indeed exactly what we'll do
3332.218 -> over the next minutes.
3333.3 -> Now, what do we want to do here in action?
3336.639 -> Of course, we wanna get the data entered into this form
3339.401 -> and then store it as a new note in this notes.json file
3343.541 -> with help of these utility methods here.
3346.26 -> For that, we first of all need to extract
3348.57 -> the data submitted by that form.
3351.33 -> And thankfully, that's super easy.
3353.91 -> If you're using a form as we're doing it here,
3356.19 -> then the browser out of the box
3359.34 -> takes all the inputs that are in the form
3361.92 -> and puts them into an object
3363.3 -> which is attached to the outgoing post request.
3367.219 -> The browser does this for you,
3368.922 -> you don't have to do it manually.
3370.86 -> It will not be in json format, but some other format,
3374.234 -> but that does not matter here.
3375.871 -> The outgoing post request will include all the input data.
3380.16 -> Now you'll be able to extract the input data,
3384.01 -> with help of these names that you assigned
3385.62 -> to your inputs and text areas
3387.6 -> and whatever you have in your forms.
3390.24 -> So you must have the name attribute on your inputs.
3393.6 -> And then these name values are of course up to you.
3397.261 -> Here I chose title and content.
3400.32 -> So we don't have to extract any values
3402.66 -> in our client side React code.
3404.67 -> We don't have to use two-way binding or refs.
3408.27 -> We just submit a form
3410.479 -> and we have these names on our input elements.
3412.698 -> And then here in the action,
3414.69 -> we actually get an object provided by Remix
3419.418 -> because Remix is the thing calling this action function.
3422.01 -> And Remix gives us a data object
3424.74 -> that includes various pieces of information.
3427.94 -> Most importantly, this data object includes a request object
3433.2 -> with details about the request
3435.27 -> that was submitted by the form.
3439.156 -> And here I'll simply use object destructuring
3441.898 -> to directly get access to that request object like this.
3445.98 -> Now this request object has a form data method
3450.298 -> which we can execute.
3451.68 -> This actually returns a promise.
3453.42 -> And to use async here,
3455.916 -> I'll add the async keyword in front of this function,
3458.13 -> which is okay.
3460.03 -> This action function can be an async function
3462.273 -> or a non async function.
3463.11 -> It does not matter.
3465.36 -> And I'll then, I'll wait calling request form data.
3470.13 -> The result is a form data object,
3472.44 -> which actually gives us access to the input values
3476.1 -> that were entered into these form inputs.
3480.375 -> So that's how we can extract that submitted data
3483.51 -> from the incoming request.
3486.695 -> And we're embracing a lot of web standards here,
3489.15 -> the standard request object
3490.98 -> that's constructed by the browser and sent to the backend
3495.095 -> and the standard form behavior that's built into browsers.
3498.48 -> We don't reinvent the wheel by writing our own logic.
3501.51 -> Instead, we embrace these standards
3503.79 -> and we get access to all this submitted data
3506.31 -> with 1 simple line of code here on the backend.
3510.812 -> And keep in mind, this is on the backend here,
3513.292 -> since it's in the action function.
3515.711 -> Now that's the first step.
3516.544 -> As a next step, I'll add a note data object,
3520.476 -> brand new JavaScript object where I add a title property.
3526.35 -> And then we can access this form data object here,
3529.53 -> which we got by calling form data like this.
3532.59 -> And there we can call the special get method
3535.44 -> to extract our values by the name
3538.67 -> we assigned to the input elements.
3541.394 -> So here, since I used title and content
3543.84 -> as names for my input elements,
3546.15 -> I can use title for extracting the title
3549.72 -> and content for extracting the content.
3552.72 -> And I store these extracted values in this object.
3556.972 -> Alternatively, there is a shortcut
3558.48 -> you can use to build in object,
3561.06 -> object with a O at the beginning and call from entries
3566.252 -> and pass your form data object to it.
3569.4 -> This will basically convert this form data object,
3572.61 -> which would require the use methods
3575.964 -> to extract the data that's submitted
3578.79 -> into a standard plain JavaScript object.
3582.69 -> And this JavaScript object
3584.46 -> will have a title and a content property
3588.15 -> because those were the names attached
3590.914 -> to the inputs in the form.
3593.372 -> So that's how we get hold of the user input.
3595.59 -> Now, here we could add validation,
3598.23 -> but we won't do so for the moment.
3600.87 -> And instead, I now wanna add this note data
3605.554 -> as a new note to my existing notes.
3608.652 -> To do that, I'll first of all get hold of my existing notes
3611.52 -> that might be stored in this notes.json file already
3615.06 -> by calling GetStoredNotes.
3618.06 -> And this is a function that must be imported
3620.34 -> from this data folder and then notes in there.
3624.54 -> So I'm using one of the two utility functions
3627.11 -> to find in this data folder in the notes JS file.
3631.86 -> Now GetStoredNotes actually returns a promise.
3634.77 -> So here I'll wait it like this.
3638.156 -> And with that I got my existing notes
3640.32 -> that might have been stored before
3642.27 -> because I of course don't wanna lose them
3644.812 -> if I add a new one.
3645.9 -> Then I take my note data object here
3649.8 -> and I add a brand new property to it, an ID property
3654.851 -> because I wanna have a unique identifier for every note.
3657.51 -> And here I'll simply use the current date
3660.808 -> converted to an ISO string as an ID.
3664.135 -> Theoretically it's not unique
3665.814 -> because two notes could be added at exactly the same moment,
3668.37 -> but for this demo it's good enough.
3671.288 -> So with that, we now got the notes data,
3674.412 -> we got the existing notes.
3675.245 -> Now we can create an updated notes object
3678.15 -> by using the existing notes and calling Concat on it
3682.05 -> and concatenating this note data with the new note.
3686.91 -> And then this updated notes object
3690.191 -> should be stored back into notes.json.
3693.154 -> And that can be done with that second utility function,
3695.82 -> the store notes function, which expects to receive
3698.94 -> the new notes that should be stored.
3701.61 -> And therefore here we now call store notes,
3704.91 -> which is also imported from the data folder
3708.36 -> from the notes JS file and two store notes,
3714.135 -> we pass our updated notes.
3716.615 -> Now this all the returns are promise
3719.37 -> and I will await this promise
3721.655 -> because now there's one last thing we have to do
3724.411 -> in this action, and what would that be?
3726.96 -> Well, assuming that that all works,
3729.12 -> we thereafter might want to redirect the user.
3733.11 -> Actually for this page, we don't need to redirect the user
3736.38 -> because we'll render a list of notes
3738.984 -> right below form here in the future.
3741.36 -> But on other pages you might have a separate page
3744.06 -> for creating data, for creating notes or blog posts
3748.56 -> or whatever your website is about
3750.63 -> and then another page for listing that data.
3754.728 -> Therefore, it is quite common
3756.072 -> that you wanna redirect the user
3757.44 -> after doing something on the server
3759.87 -> after a post request that was sent to the server.
3763.38 -> And hence here you should return a value
3766.08 -> in your action function.
3767.79 -> And what you return here typically should be a response.
3772.2 -> It doesn't always have to be a response,
3774.42 -> but a typical use case is to return a response
3777.33 -> that redirects the user to a different page.
3780.96 -> And such a redirect response can be created with these
3784.41 -> by calling their redirect function,
3787.32 -> which is provided by Remix.
3790.827 -> So here I'm importing the redirect function
3792.42 -> from Remix run note.
3795.34 -> This is a function that can be executed like this
3798.81 -> and it will simply create a new response object
3801.6 -> that redirects the user.
3804 -> Here you then provide the path
3805.77 -> to which you wanna redirect the user, and that's it.
3810.21 -> With that we added this action
3814.004 -> that does extract the user data
3816.392 -> and then store that user data in a database
3819.49 -> or in this case in a file
3820.47 -> and then redirect the user to a different page
3823.89 -> or to the same page in this case.
3826.83 -> With all that done, if we save everything,
3829.35 -> that's already all we need.
3831.96 -> If we now open those developer tools again
3834.18 -> to see whether things work,
3836.551 -> and I then add test and test
3840.27 -> and zoom out a bit here and click add note.
3844.59 -> You see the page was reloaded here
3848.25 -> and we got a request here with a 300 status code,
3852.152 -> which is that redirect that occurred.
3854.82 -> That's the request that led us to this new page
3857.7 -> that was loaded here.
3859.8 -> Now we can tell that the note
3861.72 -> was actually stored on the backend
3864.76 -> by the fact that we got no error
3865.92 -> and by taking a look at notes.json.
3870.768 -> And in there you see this is the new note
3874.507 -> that was added with the content we entered in the form.
3878.16 -> So now we got a full stack app with some server side code
3883.307 -> that is not executing on the browser side,
3884.7 -> but on the backend.
3886.89 -> Over the last minutes,
3888.83 -> we got started writing some backend code
3890.61 -> by handling the submission of this new note form here.
3895.38 -> Now we could add validation here, user input validation,
3899.71 -> and we will do that a little bit later.
3901.768 -> But for the moment, I instead wanna make sure
3904.99 -> that we can also fetch the data
3906.808 -> which we're storing in this notes.json file.
3909.891 -> Because submitting data is nice,
3910.724 -> but of course we don't see that here in our application
3914.34 -> and ultimately we want to build an application
3917.07 -> users can use.
3919.32 -> So therefore the next goal is to output a list of goals.
3924.227 -> And for data attached you find a new component,
3927.048 -> the notelist.jsx file and a notelist.css file,
3931.86 -> which you should also put into your app components folder
3936.18 -> because this again will not be a route
3938.79 -> but instead a component we use in another component,
3941.67 -> in this case, in another route component.
3944.984 -> If you take a look at this new note list component
3948.806 -> in this new notelist.jsx file,
3951.507 -> you see that it expects to get a notes prop
3953.727 -> and then I map through all my props
3956.4 -> to then simply output some data about my notes.
3960.427 -> For example, output the date
3961.77 -> which is encoded in the ID of every note
3964.8 -> due to the logic we wrote here in this action,
3968.28 -> we're adding the date as an ID.
3971.31 -> And we're also outputting the title and the content.
3974.25 -> That's what I'm doing here.
3975.57 -> I'm also importing some styles from note list CSS
3979.988 -> in this note list JSX file,
3981.67 -> and then I surface those links
3983.37 -> by exporting a links function.
3986.61 -> Therefore we can now go back to notes.jsx
3989.887 -> and there import this note list component
3994.48 -> and import those surfaced links as well.
3997.44 -> So I import notelist from ~components notelist
4003.77 -> and from that same file I also import those links
4007.19 -> and I give it an alias of noteListLinks.
4011.45 -> By the way, now you also see why I'm using aliases here
4014.87 -> because the function which is exported
4017.57 -> by those component files is always called links
4021.582 -> and therefore we would've a name clash
4022.97 -> if we wouldn't assign aliases.
4026.87 -> Now I just need to add those links
4029.42 -> exported by this component file
4031.1 -> to the links made available by this route
4033.53 -> and therefore in the links function of this route,
4035.96 -> I just also spread my note list links
4040.46 -> into this array like that.
4044.023 -> So now this route, when it's loaded,
4045.74 -> will load all the CSS files that are needed
4048.26 -> for all the components that we use in this route component.
4052.621 -> Speaking of that,
4054.363 -> I of course want to use this note list component
4057.56 -> and here my idea simply is that I output my note list
4061.7 -> below my new note like this.
4065.9 -> However, this note list component of course, as mentioned
4069.53 -> needs a notes prop.
4072.02 -> It needs to get some notes that should be output
4074.12 -> and that's the part where we now need to fetch data.
4077.96 -> We need to fetch that data from this notes.jsx file
4081.92 -> and that again means we'll write some backend code
4085.922 -> because reaching out to a data source like a database
4088.52 -> or in this case this file is a backend task.
4091.88 -> This file is also stored on the backend by the way,
4095.3 -> it's not downloaded to our users,
4097.55 -> it's on the backend on our server.
4101.06 -> So therefore in the notes route
4103.4 -> we have to add more backend code,
4106.201 -> but this time it's not an action
4108.636 -> because now I don't wanna act upon some forms submission,
4110.78 -> but instead I wanna load the data
4112.76 -> whenever this component is loaded,
4114.74 -> whenever a get request reaches this route.
4118.79 -> And that's achieved by exporting another function here,
4121.76 -> the loader function like this.
4126.728 -> Loader like action is a reserve name just like links
4130.13 -> and Remix will be looking for such a function,
4133.1 -> hence we also must export it
4135.571 -> so that Remix can find it in this file.
4137.57 -> And loader will be triggered by Remix,
4139.94 -> will be called by Remix
4141.89 -> whenever a get request reaches this route,
4145.149 -> which basically means whenever we wanna view this page,
4148.215 -> whenever this component is about to be rendered.
4151.153 -> And keep in mind the component
4152.396 -> will be pre-rendered on the server.
4153.98 -> On the server it will be rendered
4155.51 -> and the finished HTML code is sent to the client then
4159.29 -> along with some JavaScript code
4161.39 -> so that we still have an interactive website
4164.42 -> for the end user,
4166.252 -> but it's prepared and pre rendered on the server.
4169.916 -> Therefore it's now in this loader
4171.713 -> where we now write more backend code,
4173.57 -> because just like the code in the action,
4175.49 -> code in the loader will be executed on the backend only,
4179.595 -> it will never reach the frontend,
4181.655 -> never reach the client side.
4184.636 -> Here I wanna call get stored notes,
4186.476 -> which is another function we import from data notes.
4189.41 -> Actually we are already importing it
4191.81 -> because we already used it here in the action as well.
4195.11 -> I'll use it again here,
4196.43 -> and get stored notes returns a promise
4198.56 -> so I'll turn my loader into async function.
4201.86 -> Just like the action, it can be async or not async.
4205.34 -> Here we need it to be async
4207.892 -> because I want use the keyword
4209.193 -> to get my notes which are stored in the notes.json file.
4215 -> And then here, that's actually all.
4217.753 -> I don't need to do anything else.
4218.586 -> I just wanna retrieve my notes
4220.76 -> and now I need to pass them to this component
4223.22 -> because I wanna output my notes there.
4225.29 -> And how does this work?
4227.215 -> Well, that's again the part where Remix makes sure
4230.57 -> that these different ends and components
4234.02 -> and building blocks work together.
4236.63 -> In our loader, we can simply return the data
4239.54 -> that should be made available to the component,
4243.316 -> so to this component in this case.
4245.21 -> Now technically, the data you return here in this loader
4249.17 -> is sent to the frontend or to be very precise,
4253.4 -> this component, this notes page component here
4256.67 -> can be rendered on the server side
4259.04 -> if it's the first request to this page
4261.95 -> by a given visitor, for example.
4264.41 -> Thereafter the visitor gets a single page application
4268.04 -> in which he or she navigates around,
4270.05 -> but for the first request,
4271.67 -> this is rendered on the server side.
4274.31 -> And therefore this component function
4276.953 -> might also actually be executed on the server side.
4279.874 -> But if a user is already on our website
4283.13 -> and navigates around there
4284.81 -> and is therefore in this single page application environment
4288.59 -> which Remix creates for them,
4290.9 -> then Remix will just send requests
4294.196 -> to the backend behind the scenes to fetch some new data
4296.72 -> and it will then collar your loaders on the backend
4299.72 -> because that code always runs on the backend.
4302.54 -> And it will send these requests to the backend
4305.33 -> to the loader behind the scenes.
4307.43 -> Now I'm stressing this, I'm emphasizing this
4310.13 -> because this means that the data returned by the loader
4313.4 -> sometimes needs to be sent
4315.56 -> from the backend to the frontend.
4318.38 -> So there must be a response sent
4320.93 -> from the backend to the frontend.
4323.664 -> And indeed that's what Remix does behind the scenes.
4326.06 -> It wraps this data into a response.
4329.222 -> Now you can also do this yourselves.
4333.062 -> You can return a new response
4334.7 -> using the standard response constructor function,
4338.12 -> the standard response class that's built into note.js.
4342.17 -> So this is not a Remix feature.
4344.75 -> And you could pass your data to this response.
4347.45 -> You could json.stringify some data like your notes
4351.44 -> because that data must be converted to json format.
4356.219 -> And you could then add a second value, a second argument
4360.74 -> to this response constructive function
4363.11 -> to add some extra headers so that you for example,
4366.11 -> can set the content type header
4368.42 -> and set this to application json.
4372.758 -> That's an alternative to sending back the data like this.
4374.87 -> And that's in the end what Remix does under the hood,
4377.81 -> though, of course this approach
4379.61 -> where you just return the notes is much shorter.
4383.3 -> But there also is a shorter alternative
4385.61 -> to what I just showed you,
4386.75 -> in cases you still wanna send back a response manually.
4390.617 -> Remix offers adjacent function,
4393.8 -> which must be imported from Remix run note.
4397.94 -> And that json function actually takes your raw data
4402.14 -> and converts it to adjacent response behind the scenes
4405.23 -> setting that content type header
4407.15 -> behind the scenes as well, for example.
4410.475 -> So this all the returns a response instead of the raw data,
4412.88 -> but it's an easier way of returning such adjacent response.
4416.51 -> And indeed, that is what Remix will do behind the scenes
4419.24 -> if you just return raw data like this.
4422.6 -> Now here I'll stick to returning raw data
4425.12 -> and I won't use this json function,
4427.31 -> but I wanted to show this alternative to you
4429.98 -> because it is important to understand
4432.14 -> what Remix does behind the scenes
4434.21 -> and what you could instead of returning the raw data.
4438.816 -> But here I'll simply return my notes.
4441.2 -> I return my notes here and in this component,
4444.29 -> we now get access to these notes here
4446.99 -> to the data returned by the loader
4448.82 -> by using a special hook provided by Remix,
4452.09 -> the UseLoaderData hook.
4455.296 -> This hook is imported from Remix run React
4459.65 -> and this hook simply gives us access
4462.11 -> to the data returned by the loader.
4465.14 -> Now, very important, the data returned here is serialized.
4469.535 -> Its temporarily converted to adjacent string,
4472.01 -> so you can't return any rich objects here.
4475.979 -> I mean you can, but any methods or anything like that
4478.715 -> will get lost, you just get the plain data.
4482.095 -> And that's what you get here with UseLoaderData.
4485.179 -> So here I get my notes, my array of notes,
4487.07 -> and in this case nothing will be lost
4489.878 -> because my notes are just a bunch of strings in the end
4492.775 -> and that is perfectly serializable.
4495.47 -> And then the notes here can be passed
4497.96 -> to the notes prop of the note list component like this.
4503.15 -> And that is all we have to do
4505.49 -> to load data for this component.
4508.896 -> And this loader as mentioned,
4509.843 -> will be executed whenever a user visits this page.
4514.13 -> If we already visited another page
4516.32 -> and we then click a link
4518.235 -> so that we're redirected to this page,
4519.68 -> Remix will behind the scenes, execute a loader on the server
4523.34 -> and make the data available on the frontend.
4527.192 -> We don't have to worry about anything regarding that
4528.92 -> and we don't have to send our own request to a backend
4532.37 -> or do anything like this.
4534.11 -> Instead, that's one of the advantages of Remix.
4536.96 -> It seamlessly blends frontend and backend together.
4542.998 -> If we save this and we go back,
4544.94 -> we should therefore see this first note here
4547.73 -> below this input.
4550.07 -> And I'll zoom out a bit so that we can see it a bit better.
4553.978 -> Here's this note, it's not clickable.
4556.816 -> I just have this animation here,
4557.979 -> but this is the note we added before.
4560.72 -> Now what's interesting is that if I add a new note,
4563.9 -> a second note, Remix is awesome and I submit this,
4568.79 -> it will appear here automatically.
4571.613 -> So Remix takes care about automatically loading this
4574.918 -> when we revisit this page
4576.256 -> because that's exactly the idea behind loaders.
4578.78 -> The loader gets executed
4580.67 -> basically whenever it needs to execute
4584.336 -> to ensure that this page always shows the latest data.
4587.78 -> Now we added the loader
4588.92 -> and therefore we're now not just able
4591.579 -> to handle form submission but also to load data.
4594.05 -> And you might wonder how exactly
4595.7 -> do we now show a loading spinner on the front
4599.718 -> and whilst the data is being loaded?
4600.551 -> And the important answer is we don't.
4602.96 -> Instead Remix only serves the finished page
4605.81 -> once the data has been loaded.
4608.998 -> So if I for example, am on the starting page
4610.67 -> and I then click on my notes, this looks instantly,
4614.03 -> but behind the scenes Remix ensures that the data was loaded
4618.059 -> before it loaded this page.
4620.33 -> And you can again see this if you open the developer tools
4623.27 -> and the network tab.
4625.396 -> If I click on my notes, you see here behind the scenes,
4628.43 -> this is the request where the notes data was fetched.
4631.85 -> This request here is a get request
4634.7 -> triggered by Remix behind the scenes
4636.98 -> sent to a URL controlled by Remix behind the scenes
4640.7 -> and it contains the notes data
4643.376 -> which is then rendered on the screen.
4645.05 -> So Remix takes care
4646.46 -> that all the data is loaded when it's needed.
4649.835 -> We don't have to worry about that as mentioned.
4652.7 -> So at this point we're able to submit data and load data
4657.551 -> and execute the respective backend code,
4659.974 -> and therefore we're able to perform
4661.19 -> the two most important backend tasks
4664.46 -> you have in almost any full stack app you're building.
4669.205 -> I now, when I come back to this action again though,
4671.984 -> and take a closer look at this form submission
4674.3 -> at user validation and some related aspects
4677.66 -> that are important to understand and know.
4682.084 -> And for that, let's go back here and add a third note.
4686.919 -> This is another note on Remix.
4690.11 -> Now I want you to watch this refresh icon again.
4694.19 -> If I click add note, you see it briefly turned to a cross.
4698.21 -> So what happened is that a new HTTP request was sent
4703.844 -> and a new page was returned by Remix in the end.
4707.33 -> This should make some sense
4709.04 -> because we are redirecting here at the end of the action,
4711.89 -> so we are redirecting to a new page,
4714.703 -> which is the same page as before,
4716.321 -> but technically it's a new request sent by the browser
4719.801 -> due to this redirect response we're sending back.
4722.27 -> So everything's working as intended,
4723.98 -> but of course it would also make sense
4726.77 -> to stay in the single page application world here.
4730.1 -> If we are on this page and I add a new note
4733.28 -> and I submit the note,
4735.11 -> it would make sense to not send a new request
4738.68 -> that fetches a new page
4740.24 -> because that means that we also re-download
4742.46 -> all the JavaScript code.
4744.778 -> Sure, it might be cashed,
4746.004 -> but we still trigger a new download request
4748.37 -> for all the JavaScript code
4750.761 -> and we execute all that JavaScript code
4752.57 -> that's used on the client side again.
4756.037 -> And we might wanna avoid this if we're already on the page.
4759.468 -> To achieve this, we can go back to our form
4764.79 -> and replace this default form element,
4767.161 -> which generally worked fine as you saw
4769.16 -> with another form component,
4771.71 -> which starts with a capital F
4774.501 -> and which must be imported from Remix run React.
4779.183 -> Of course, we also must replace the closing tag.
4783.83 -> Now this form still renders a form as before
4786.92 -> and you can still add those attributes
4789.65 -> which you can add to the normal form element.
4793.04 -> But it behaves slightly differently.
4796.61 -> If we save everything here
4798.98 -> and I add a fourth note, another note,
4805.561 -> and you watch this refresh icon again,
4807.77 -> you see now it did not spin and yet the data was submitted
4811.31 -> and the loader was triggered
4814.063 -> even though we technically did not reload this page.
4816.83 -> We can also tell that the page wasn't reloaded
4820.244 -> because the inputs weren't cleared.
4822.14 -> Now still the loader was triggered
4824.18 -> because as I mentioned before,
4826.1 -> Remix automatically does the thing
4828.5 -> you would expect to happen.
4831.103 -> Now with this special form component being used here
4834.68 -> instead of the default form,
4836.33 -> we stay in the single page application world
4839.457 -> and Remix actually prevents the page from being reloaded,
4842.96 -> but it still behind the scenes
4844.73 -> sends a request to the backend
4847.524 -> and triggers this action, so to say.
4848.84 -> It still performs all this code and it still notices
4853.62 -> that we want to redirect to the notes page,
4855.26 -> but it does this with client site routing thereafter,
4859.481 -> instead of triggering a new request
4860.78 -> that would actually fetch a new page.
4864.29 -> And it triggers the loader again
4867.18 -> because it understands that we triggered an action
4870.644 -> and the data might have changed
4872.063 -> and it should therefore fetch the updated data.
4874.64 -> As always, you can see that if you go to the network tab
4877.73 -> and we add a fifth note here,
4880.76 -> you see, we got some requests here,
4884.964 -> no 300 request anymore, so no redirect request,
4888.14 -> but a post request,
4890.598 -> which includes the form data that we submitted under payload
4896.58 -> and then thereafter another request, a get request,
4900.83 -> which in the end triggered that loader,
4902.84 -> so which gave us the updated data.
4906.26 -> And that's what Remix does for you.
4909.135 -> Again, blending backend and frontend
4911.881 -> while keeping the backend code on the backend.
4914.15 -> And therefore typically you want to use
4916.43 -> this form component provided by Remix
4919.58 -> instead of the default form element
4921.65 -> so that you stay in this single page world.
4925.801 -> It's not bad if you use the default form,
4927.62 -> but with that we avoid the extra request.
4930.56 -> However, this form component really shines
4933.02 -> if we then use more extra features provided by Remix.
4938.69 -> Now how can we leverage this form component
4941.72 -> besides going back
4943.6 -> to the single page application experience.
4945.68 -> Well, we could for example, disable this button
4949.341 -> whilst the data is submitted.
4950.78 -> That is something you might wanna do on forms.
4953.15 -> You wanna make sure
4954.841 -> that it's not accidentally submitted twice.
4957.05 -> And you can easily do that
4958.67 -> when using this form component provided by Remix,
4961.67 -> by also using another hook.
4964.13 -> And that's the use navigation hook.
4966.95 -> Actually at the point of time I'm recording this,
4969.921 -> it's called use transition,
4970.76 -> but it will be renamed to use navigation in a future version
4974.57 -> because use transition is a hook
4977.103 -> that's now also provided by React itself.
4979.7 -> Therefore here, I'll give it an alias
4982.19 -> and you should simply check
4983.42 -> whether you have a use navigation you could import,
4986.66 -> which you can tell by the fact that you get auto completion,
4990.324 -> which I don't get here for a use navigation.
4992.87 -> If you get auto completion for a use transition,
4995.78 -> I recommend that you also rename it to use navigation
4999.38 -> so that you already get used to that new name
5002.655 -> which this hook will have in the future.
5004.6 -> Now this hook gives us a navigation object, if we call it.
5009.07 -> And this navigation object contains some useful data
5012.61 -> about ongoing requests that might be happening
5016.24 -> behind the scenes, so to say.
5018.76 -> For example, we can access the state property
5021.7 -> to find out whether we're currently submitting data
5024.58 -> or if we're maybe loading data
5026.47 -> or if we're not doing anything.
5028.484 -> We can also access the submission property
5031.93 -> to get more information about the latest form submission
5034.84 -> that was triggered,
5036.816 -> like for example, the path to which the request was sent,
5039.22 -> what the method was or which kind of data was submitted.
5043.57 -> You can also use the type here
5045.55 -> to find out whether the last navigation
5048.61 -> that was performed by a Remix behind the scenes, so to say,
5051.67 -> was maybe that you were redirected
5053.62 -> because of some action that was called.
5057.28 -> And here I'm interested in the state.
5060.402 -> I wanna check if we're currently submitting
5062.68 -> and I'll store the result in a is submitting constant
5066.91 -> and now I can use this constant to disable this button
5070.39 -> by simply binding the disabled prop
5073.519 -> or setting the disabled prop here to is submitting.
5076.18 -> If I am submitting, I wanna disable this button.
5079.284 -> We can also change the label of this button
5082.42 -> and check if we are submitting,
5085.161 -> in which case we could say adding dot, dot, dot.
5088.93 -> And otherwise we say add note.
5092.34 -> So we can take the current submission status into account.
5098.761 -> Now here we won't really see this
5099.7 -> because this will be super fast
5102.564 -> since it's all running locally.
5103.72 -> But if you would add a new note here
5106.564 -> and your backend would be slightly slower,
5108.19 -> you would see that temporarily this button is disabled
5111.67 -> and it's labeled changes.
5114.244 -> Again, here it's just too fast
5116.185 -> since it's all happening on the same machine.
5118.921 -> We can see it in action though
5120.484 -> by awaiting a new promise which I construct here
5124.041 -> where I will simply wrap a call to set time out
5132.7 -> and then when the timeout is done, simply call resolve
5140.41 -> and set the time out to two seconds like this.
5144.19 -> With this line added,
5146.541 -> we'll simply add a two second pause here.
5149.59 -> And if we do that and I add another note,
5153.88 -> you will see that now this is disabled and shows adding
5157.12 -> until it's done.
5159.97 -> Now of course I'll comment this out
5163.503 -> since I don't want this pause anymore,
5164.68 -> but that is how you can also leverage this form component
5168.19 -> to get this extra information
5169.96 -> and to enhance the user interface
5172.3 -> based on the current submission status.
5175.45 -> So that was the use navigation and this form component,
5179.945 -> which you don't have to use in conjunction,
5181.06 -> but which allow you to enhance the user interface
5184.505 -> if you do so.
5185.661 -> You can't use use navigation with the regular form
5187.9 -> because there a new request gets sent
5190.687 -> and the page is simply reloaded
5191.56 -> instead of staying in that single page application world.
5195.581 -> You only do that with this form with a capital F.
5199.33 -> However, that's not all we can do with this action
5201.88 -> and the form data.
5203.38 -> Instead now I also wanna validate the submitted data,
5207.13 -> not some super complex validation,
5209.17 -> but at least some validation.
5211.9 -> And for this here,
5213.07 -> we could check if in that note data which we get,
5215.92 -> so that form data that was submitted, if there the title,
5220.66 -> if we trim it to remove white space at the beginning and end
5224.74 -> if that has at least a length of let's say five characters
5228.28 -> to enforce a title length of five characters.
5232.72 -> We could also add validation for the content.
5235 -> But here I wanna keep it simple
5238.1 -> so I'll just validate the title for this minimum length.
5242.35 -> Now if this is not met,
5244.3 -> so actually I wanna check whether it's smaller than five.
5247.03 -> So if it's shorter than five characters, if that's the case,
5251.567 -> if we have an invalid title, if it's too short,
5253.42 -> shorter than five characters,
5255.58 -> then we might wanna show an error or message to the user.
5259.6 -> Now you could think about
5260.8 -> calling the default alert function,
5263.609 -> which is offered by browsers.
5265.3 -> However, this wouldn't work here because keep in mind
5268.546 -> that this code runs on the backend,
5270.889 -> on a server not in the browser.
5273.188 -> So you can't use browser APIs here,
5275.627 -> you can't use browser only features here.
5278.11 -> What we can and should do instead
5280.21 -> is that we return a value here.
5283.54 -> We do return a redirect here at the end of the action,
5286.87 -> here I wanna return a different value.
5289.96 -> And what would that value be that I return here?
5293.68 -> Well, that's up to you,
5295 -> just as you can return any kind of data from a loader.
5298.78 -> Here, for example, we could return a message
5302.47 -> where we say invalid title
5305.02 -> must be at least five characters long, something like this.
5314.986 -> So actions like loaders can also return data,
5318.85 -> not just redirect responses.
5321.73 -> That's the typical thing you do
5324.202 -> at the end of a successful action
5325.51 -> but you can also return data.
5328.45 -> But how do you get hold of that data?
5331.962 -> How do you get access to that data?
5335.2 -> Well, for that there is another hook which you can use
5339.188 -> and that's the UseActionData hook.
5341.98 -> This hook like UseLoaderData,
5344.35 -> gives you access to the data,
5346.03 -> in this case returned by an action.
5348.64 -> So UseActionData gives you access
5350.68 -> to the data returned by an action.
5352.93 -> UseLoaderData gives you access
5355.284 -> to the data returned by an loader.
5358.185 -> UseActionData also must be imported from Remix run React.
5364.203 -> Now this data can again be anything.
5366.324 -> In our case it's an object with a message property.
5369.761 -> And we can now use that to output the error message.
5373.81 -> We could for example, pass it to new note as a prop,
5377.71 -> and then in the new note component
5379.87 -> we could output this error,
5382.42 -> maybe here above the form or here inside the form.
5386.95 -> Here at the top we could add a paragraph
5390.1 -> where we output the error message.
5394.186 -> We could, as I mentioned, pass down this data
5396.82 -> via a prop to new note, but we don't have to.
5400.69 -> Instead we can remove the UseActionData hook
5403.48 -> from this notes page, so from this route component
5407.083 -> and remove this import
5408.61 -> and instead call it in this new note component itself,
5413.02 -> because that's all is important.
5414.49 -> UseActionData and also UseLoaderData
5417.7 -> cannot just be called in these route components
5420.67 -> but in any components
5422.56 -> and they will get the action or loader data
5426.443 -> of the closest loader or action that was called.
5431.483 -> So if I use UseLoaderData
5432.73 -> or I UseActionData in the new note component
5436.363 -> and this component was rendered by this page,
5438.16 -> I will get access to the data returned by the loader
5440.86 -> or the action of that page.
5444.37 -> So therefore here in the new note.jsx file,
5447.1 -> I can access UseActionData, get my data,
5451.3 -> and of course import that hook from Remix run React.
5457.355 -> And now we can leverage that data to output our error.
5461.74 -> For that I'll output this paragraph conditionally
5465.67 -> and first of all, check whether data exists.
5470.565 -> And if it does, whether it has a message.
5475.106 -> If that's the case, I will output data.message here.
5481.547 -> If that's not the case, I won't output anything.
5484.912 -> And this is all default JavaScript JSX code.
5489.806 -> If we save that and we go back here,
5492.4 -> and I enter test which is too short
5495.76 -> and something here which doesn't matter,
5498.25 -> and I click add note, I get my error message here,
5502.99 -> as expected you to UseActionData.
5507.13 -> If I add test note instead, which is long enough,
5510.931 -> it is added correctly and the message disappears
5514.035 -> because the action data is clear thereafter
5516.229 -> because the action was called again and succeeded this time
5518.71 -> and returned a redirect instead of my error data.
5524.564 -> But it is important to understand
5525.46 -> that actions can also return data,
5528.04 -> that you can get hold of that data with UseActionData
5530.86 -> and that UseActionData, just like UseLoaderData
5534.13 -> can be called in any component.
5537.848 -> At this point, we already made good progress
5539.56 -> and we're almost done for this essentials section.
5544.06 -> However, there are still a couple of crucial features
5547.95 -> that are missing and one of them is error handling,
5551.09 -> because things can go wrong.
5552.61 -> Here, for example, in our action,
5554.59 -> the file or the database where we're storing data
5559.032 -> could temporarily be unavailable
5561.386 -> or overwhelmed or anything like this.
5563.5 -> So saving the data could fail
5565.42 -> and of course, reading the data could also fail.
5568.93 -> Now to simulate this, we could delete this file here,
5572.77 -> but here I'll not delete it, I'll just rename it
5575.2 -> so that it's not found anymore.
5577.24 -> I deleted the end here at the end of the extension
5580.03 -> and therefore this file,
5582.19 -> this notes.json file for which we're looking
5585.368 -> does not exist anymore.
5587.14 -> Therefore, now if we save this and I reload,
5590.26 -> I get this ugly error page
5593.989 -> because our server essentially crashed
5596.787 -> or we got an error at least,
5598.83 -> and this default error page is being shown.
5601.39 -> Now, typically you don't wanna use that default error page.
5605.891 -> Instead you wanna show your own error page
5608.008 -> and you can easily do that with Remix.
5610.6 -> For example, we can go to this root component
5613.21 -> and in there we can export another new function.
5616.75 -> And that function is called ErrorBoundary like this.
5621.651 -> And it's actually a component which we're exporting here
5624.392 -> therefore it starts with an upper case E.
5627.19 -> This is a special function, a special component though,
5631.176 -> which must be named like this
5632.97 -> because just like links and action and so on,
5634.51 -> Remix will be looking for this.
5637.31 -> This is a component Remix will display
5639.47 -> if an error occurs anywhere in your application
5642.83 -> so on any page,
5643.663 -> since I'm doing this in the root component here.
5646.03 -> I will soon show you
5647.95 -> how you can add it to other pages as well.
5650.17 -> But here we'll do it in the root component.
5653.23 -> Now in there I now wanna return my HTML skeleton,
5659.352 -> so I'll grab that, but I'll adjust it a little bit.
5663.155 -> For example, I'll add a title here which we can do.
5666.035 -> We can add any default HTML elements here in this skeleton.
5669.22 -> And here I'll add a title to head and say an error occurred.
5676.06 -> And then here I still output my main navigation maybe,
5680.691 -> but instead of the outlet, I'll output a main section here
5684.82 -> where I have a H1 element with a text of an error occurred.
5691.181 -> And then below that maybe the error message.
5694.888 -> Now this special component always gets an error prop,
5699.944 -> which we can get hold of
5701.351 -> with object destructuring like this.
5703.42 -> And this prop will be provided
5705.31 -> or a value for this prop will be provided by Remix
5709.774 -> because Remix will create this component
5712.254 -> when an error occurs.
5714.771 -> This is the default JavaScript error object.
5717.01 -> So the value here on this error prop is an object
5721.23 -> based on this default JavaScript error object constructor
5724.66 -> and therefore we'll have a message property
5727.854 -> which we can access here.
5729.73 -> We could then also add some extra text
5732.995 -> which maybe says back to safety
5735.79 -> and safety should maybe be wrapped by the link component.
5741.147 -> So this default Remix link component
5743.32 -> which therefore also must be imported from Remix run React.
5750.987 -> And here I simply want to go back to the starting page.
5756.368 -> With that, if we save this and we reload,
5759.086 -> I get my own error page here, as you can tell.
5762.486 -> Now, the styling is a bit off,
5763.75 -> but it can be fixed by adding a special class to main here,
5766.15 -> the error class for which I define some styles
5770.47 -> in one of the style files I provided earlier.
5773.32 -> With that, I got this error look here
5776.74 -> and I can of course go back to safety,
5779.08 -> but whenever I go back to notes, it crashes again.
5782.35 -> So that's how you can add your own error page,
5785.771 -> your own error component with help of this ErrorBoundary.
5789.43 -> However, you are not limited
5791.23 -> to just adding this to the root component.
5794.907 -> Instead you can add this,
5796.128 -> this special ErrorBoundary component in any route file.
5801.088 -> If you only have it in the root component,
5802.36 -> this will simply handle all errors that occur anywhere.
5806.866 -> So it's the last resort of error catching, so to say.
5810.94 -> But if we know that things could go wrong
5814.39 -> on our notes route here,
5815.563 -> it might make sense to add a separate ErrorBoundary here.
5818.74 -> And then here we could return some content
5821.43 -> that's tailored for this specific page.
5824.987 -> And actually what's then important
5826.251 -> to keep in mind and to understand
5827.53 -> is that if you have an ErrorBoundary
5830.65 -> on a route that's not the root route,
5833.38 -> only the content of that route which failed
5836.92 -> will be replaced by the ErrorBoundary.
5840.667 -> So only this page content will be replaced.
5843.76 -> So if I have an ErrorBoundary here in notes.jsx,
5847.18 -> this content returned by this ErrorBoundary component
5851.173 -> will be injected in my default skeleton here
5855.334 -> in place of the outlet.
5858.126 -> So therefore now with that I could copy this part here
5861.46 -> and just this part of my root error outlet
5865.51 -> and add it here and return it
5868.15 -> as part of this ErrorBoundary.
5871.03 -> We now just need to make sure
5873.211 -> that we again use this error prop
5876.284 -> which we're getting automatically.
5879.447 -> I now also must import link here from Remix run React again.
5886.166 -> And with that added, we get the same output as before.
5888.82 -> But technically what happens under the hood is different now
5893.65 -> because now we're actually using this ErrorBoundary.
5897.49 -> And therefore we could also tailor this
5900.4 -> more for this specific scenario.
5901.93 -> And for example, replace an error occurred
5905.371 -> with an error related to your notes occurred,
5911.75 -> whatever you want.
5913.885 -> And now you will see that this ErrorBoundary is being used
5916.15 -> because you see this message here.
5920.08 -> So that's how you can handle errors
5922.955 -> and how easy it is to handle errors.
5925.409 -> You don't have to manage any error state in your component
5928.42 -> and check if there is an error or anything like this.
5931.39 -> You simply define this fallback component
5934.54 -> and it will automatically be rendered
5936.31 -> whenever an error occurs in this route
5940.204 -> or in case of the root component here,
5942.928 -> the root route anywhere in the application.
5946.768 -> Now ErrorBoundary is super important and helpful
5949.493 -> for well, handling errors,
5951.611 -> but it actually only deals with normal errors.
5955.728 -> But there is another kind of error that could occur
5958.36 -> in case of a full stake application.
5961.791 -> You could have an error response
5963.55 -> that's being generated by the backend.
5966.58 -> And to show you what the difference is,
5968.32 -> let me actually generate an error response.
5972.13 -> For that, back here in notes.jsx, we could say
5976.048 -> that we maybe wanna generate some error response
5978.13 -> if we don't find any notes yet.
5981.637 -> We could check if maybe not notes
5983.728 -> or if notes.length is equal to zero,
5987.157 -> which means we have no notes yet.
5988.18 -> And in that case I don't wanna return this empty array,
5991.36 -> but instead I wanna generate an error response,
5994.688 -> maybe a 404 response because no notes were found.
5998.71 -> And generating such a error response in Remix
6002.348 -> is super simple.
6003.472 -> You don't use return but instead the throw keyword
6005.76 -> and then you don't throw any random data.
6008.64 -> But instead a response,
6010.62 -> which you can create by instantiating
6012.66 -> the built in response object,
6015.47 -> which is supported by note JS and the browser
6018.795 -> or by using a shortcut and using the json function,
6023.811 -> which is imported like the redirect function
6027.548 -> from Remix run note.
6029.25 -> And this json function simply generates a response object
6032.82 -> which contains some json data.
6036.12 -> Now the first argument to this json function
6039.274 -> is the data that should be included in the response.
6041.58 -> And here that could be an object with a message of
6044.16 -> could not find any notes, for example.
6048.21 -> The second argument you pass to this json function
6051.45 -> allows you to configure this response in greater detail.
6054.9 -> For example, you can add a status code
6058.352 -> and here we could set it to 404 to make it clear
6061.089 -> which kind of response it is,
6063.266 -> which kind of error we have here.
6065.79 -> You can also add a status text which could say not found
6071.376 -> or anything like this.
6072.93 -> Now whenever you throw a response,
6075.27 -> Remix recognizes this and renders a different component
6079.41 -> than the ErrorBoundary.
6082.155 -> Whenever you throw anything but a response,
6084.03 -> like some text or a regular object,
6086.64 -> the ErrorBoundary will be used.
6089.46 -> Whenever you return,
6091.35 -> the default page component will be used.
6094.17 -> And whenever you throw a response,
6096.63 -> like a response generated with this json helper function
6100.32 -> another component will be rendered,
6102.63 -> a component which you also should export here
6105.54 -> in your route to which it belongs,
6107.94 -> and a component that's called catch boundary
6112.403 -> because this catches any error responses.
6116.25 -> And just like ErrorBoundary,
6118.862 -> you can add it on a per route level
6120.6 -> or add it on the root level or do both.
6124.29 -> The root level catch boundary
6126.63 -> will catch all unhandled error responses
6130.843 -> that were generated anywhere else in the application.
6133.32 -> And the route specific catch boundary
6136.325 -> will handle any error responses related to this route here.
6140.25 -> And here, we could then return, again a main block
6145.41 -> where we simply output a paragraph
6148.53 -> where we then might want to output the message
6151.44 -> that's attached to this error response.
6155.28 -> To get hold of that data,
6157.14 -> we can use another special hook provided by Remix,
6160.885 -> the useCatch hook,
6162.72 -> which of course must be imported
6164.46 -> from Remix run React in this case.
6168.84 -> Now this useCatch hook here gives us an object,
6173.79 -> the caughtResponse to be precise.
6176.31 -> So this error response that was generated
6179.07 -> or to be precise and object based on that error response.
6183.39 -> And on that object we can get hold of the status code,
6187.967 -> the status text and the data that's part of the response.
6192.411 -> So basically the kind of things we passed
6194.448 -> to this json function, right.
6196.368 -> There, I passed some content, a status and a status text
6198.96 -> and that's exactly what we can extract
6201.09 -> with help of that data returned by useCatch.
6205.41 -> So here we could then access the data
6208.26 -> and get hold of the message property if data exists.
6213.78 -> And if the message property or data is undefined,
6216.66 -> we could simply use some generic fallback message
6219.66 -> like data not found,
6222.96 -> and then stored that in a message constant
6226.091 -> and output this here in our returned value.
6229.968 -> And this component will now be rendered by Remix
6232.688 -> whenever we have an error response being thrown
6235.56 -> by an action or a loader related to this route.
6242.421 -> If we now save this and I fix the notes.json file name again
6245.97 -> and I now delete all the notes from this file
6250.763 -> so that we have no notes.
6252.966 -> And we then save everything again.
6256.496 -> If we reload, we see this
6259.579 -> could not find any notes error message,
6261.84 -> which is that message we attached
6264.847 -> to this error response we're flowing here.
6269.52 -> Now styling is not great but we can easily fix this
6272.91 -> by adding a special class to this paragraph,
6275.22 -> the info - message class,
6278.628 -> which is also defined in one of the CSS files.
6281.37 -> And with that, it looks a lot better.
6284.13 -> Of course here we now have the problem
6286.708 -> that we can't add any notes anymore
6289.071 -> because well our form is gone
6290.79 -> and therefore here in this component,
6293.64 -> in this catch boundary component,
6295.41 -> we also might wanna output the new note form
6299.273 -> so that we still can add new notes like a new note here.
6306.153 -> And this then still works as before,
6307.508 -> but now we also got this error response handling here.
6311.673 -> And this error response handling
6313.588 -> can also be used in the root route again
6314.76 -> to return some generic skeleton for all error responses
6320.009 -> that are not handled anywhere else.
6321.24 -> And most importantly also for the scenario
6324.809 -> that users entered invalid URL here,
6327.21 -> because in that case,
6329.273 -> if I for example try to visit localhost 3000/ABC,
6333.21 -> Remix will automatically generate an error response.
6337.35 -> However, we then also have to handle that error response.
6341.743 -> And for that we can also use useCatch here
6343.38 -> in this catch boundary in the route JSX file,
6346.77 -> and of course import useCatch from Remix run React,
6355.239 -> then get hold of that caught response
6357.75 -> and then maybe use the caught response status text
6361.879 -> as a title here and as a title here.
6365.084 -> And check if we have a caughtresponse.data message
6370.92 -> and otherwise just output something generic
6375.303 -> like an error occurred or something went wrong.
6382.759 -> That is something we could output here.
6384.21 -> With Dev, we also have this catch boundary
6387.438 -> in the route JSX file.
6388.657 -> And now if I try to visit a page that's not supported,
6391.41 -> I see this page here
6393.63 -> because of this catch boundary we just added.
6397.932 -> So now we can add notes, we can handle errors
6400.23 -> and this overall application is taking shape
6403.521 -> and we also already learned quite a bit about Remix.
6407.61 -> One crucial feature is missing,
6410.663 -> which definitely also must be covered
6412.599 -> in this essentials section here
6415.639 -> and that would be dynamic routes.
6417.98 -> Now what are dynamic routes still?
6420.21 -> At the moment we have two routes.
6422.4 -> And now let's say we wanna make these notes clickable
6425.49 -> and we wanna show these note details
6427.56 -> in some full screen mode if we do click them.
6432.039 -> So then we need a new route
6433.714 -> that shows us these note details.
6435.42 -> And of course, it would be one at the same route
6437.7 -> and one at the same component,
6439.77 -> one at the same page for all the notes.
6443.159 -> But the content would be different
6445.079 -> because if we have multiple notes,
6445.912 -> like remix is awesome as a second note, it really is,
6450.78 -> then of course if I click this note,
6453.18 -> I wanna see the details of this note
6455.43 -> on the full screen page.
6457.5 -> And if I click the first note, I wanna see those details.
6459.75 -> So one route but with different content.
6462.237 -> And that's exactly what a dynamic route is.
6465.761 -> Now we add such a dynamic route
6468.33 -> by adding a new file to the routes folder
6470.55 -> and giving it a special file name,
6472.44 -> a file name that starts with a dollar sign.
6475.783 -> And here that could be noteID.jsx for example,
6481.82 -> though the part after the dollar sign is up to you.
6484.2 -> It could also be just ID or note or whatever you want,
6488.13 -> but here I'll go for noteID.
6490.38 -> The dollar sign in front of noteID signals to Remix
6495.15 -> that this is a dynamic route.
6498.076 -> And a dynamic route simply means that
6499.835 -> the actual value in the path
6501.06 -> will not be dollar sign noteID,
6503.4 -> but instead that only acts as a placeholder
6506.636 -> for the actual value that will later be imported in the URL.
6510.63 -> And you probably already know this concept of dynamic routes
6514.636 -> from a React router, express JS
6516.33 -> or a lot of other routing libraries as well.
6521.057 -> Now here in this dollar sign noteID.jsx file,
6523.74 -> we again should export a default function,
6526.86 -> maybe called note details page.
6529.44 -> Name of course, is up to you, as you know.
6532.14 -> And there I wanna return some markup.
6535.26 -> I wanna return a main element
6537.58 -> with an ID of note - details,
6541.001 -> which then includes a header maybe.
6544.05 -> And in there a nav element
6545.73 -> where I simply have a link imported from Remix run React,
6551.28 -> which points back to slash notes,
6553.35 -> so to that overview page where we add notes
6557.036 -> and see that list of notes.
6558.78 -> and we could give this link a label off back to all notes.
6563.46 -> And then below this nav element here, I have a H1 element
6568.68 -> where I wanna output the note title.
6571.29 -> So this should actually be dynamic,
6574.113 -> it will be replaced soon.
6575.67 -> And below the header I wanna have a paragraph
6578.7 -> with an ID of note - details - content,
6583.756 -> and then here the note content.
6586.59 -> Now these IDs are important for styling purposes
6589.92 -> because attached you also find a note-detail.css file,
6594.796 -> which you should move into your styles folder.
6597.662 -> And then in the noteID JSX file,
6602.092 -> the dollar sign noteID.jsx file,
6604.08 -> you should import that CSS file, like this for example,
6611.85 -> and then export this special links function
6616.183 -> you already learned about earlier
6618.009 -> where we have one style sheet link
6622.08 -> which simply loads those imported styles
6625.745 -> so that we do load these page specific styles.
6630.308 -> Now this is my note details page,
6632.004 -> but of course, the data is missing.
6634.23 -> Now before we add the missing data,
6637.023 -> let's make sure that we can reach this page.
6639.321 -> And we can reach it already since it is a registered route,
6641.97 -> by going to our website and for example, entering note-1.
6646.92 -> So not notes but note-1.
6650.37 -> Instead of getting a 404 error,
6652.62 -> we now see this notes detail page
6655.32 -> because that's what dynamic routes are all about.
6657.93 -> This value note-1 is now interpreted by Remix
6662.88 -> as a value for this noteID placeholder,
6666.9 -> and it's this dollar sign that tells Remix
6669.24 -> that this is a placeholder.
6672.825 -> So whatever we enter here, including ABC
6675.33 -> is now treated as a value for this noteID.
6678.96 -> We can still enter slash notes.
6681.18 -> Remix is smart enough to understand
6683.7 -> that there is a dedicated route for the path notes,
6687.66 -> but basically anything else is treated
6690.703 -> as a value for this noteID.
6693.604 -> So that's how we can register such a dynamic route.
6696.27 -> To also go there dynamically,
6698.52 -> we can go to the note list JSX element
6701.52 -> and wrap this article with the link component
6705.81 -> which is imported from Remix run React.
6713.924 -> Move the closing tag here
6716.361 -> and then the two prop of this link points to the noteID
6721.583 -> for which we wanna load this detail page.
6724.802 -> Here this means that I'll set this to a dynamic value
6727.41 -> and I'll use noteID as a value.
6732.48 -> Out of the box, the link component
6734.948 -> supports relative paths here for the two prop,
6737.58 -> which means if I enter something like note-1 here,
6742.308 -> this is automatically appended
6743.822 -> after the currently active path.
6746.527 -> If I add a slash at the beginning,
6747.982 -> it wouldn't step be an absolute path,
6750.201 -> which in this case would be the same,
6751.903 -> but it can be useful for easily constructing longer paths
6754.23 -> by just adding some new fragment here
6756.9 -> and letting the link and Remix automatically appended
6760.32 -> to the currently active path.
6763.491 -> And here that's what we'll do with noteID.
6765.363 -> It's simply appended to the currently active path.
6768.96 -> Which means that if I save this, if I click this,
6772.723 -> it tries to go to slash notes and then this ID,
6777.731 -> because as I mentioned,
6778.742 -> it adds it to the currently active path.
6780.947 -> And since I am on slash notes here,
6782.07 -> it adds the ID after this.
6785.19 -> Now since this doesn't lead us to the note,
6787.725 -> we have two options.
6788.558 -> We rename this file to make sure
6791.083 -> that it does start with slash notes,
6794.13 -> or we change our link here
6796.8 -> and convert it to an absolute path.
6800.227 -> For example, like this,
6801.27 -> by adding a forward slash at the beginning.
6804.99 -> However here I rather rename this route name.
6808.56 -> We can do this and add a leading slash notes
6812.25 -> in front of this dynamic segment
6814.44 -> by adding notes. in front of this dollar sign, NoteID part.
6819.87 -> This might look a bit strange
6822.123 -> but is a valid code when using Remix JS.
6825.27 -> This simply tells Remix
6827.1 -> that this route here should be loaded
6830.4 -> if the path is slash notes slash some dynamic segment.
6838.472 -> So by changing this file name like this,
6840.232 -> we see that now we do load this note detail page
6844.611 -> for a slash notes slash the ID of that note.
6846.9 -> And if on the other hand
6848.701 -> I try to visit localhost 3000 slash note-1,
6851.1 -> I get my 404 page again basically.
6855.48 -> So that's how we can load the note detail page
6859.069 -> and how dynamic routes work in general with Remix.
6863.109 -> How can we now also load the data
6865.41 -> that's required by that page though.
6867.69 -> To load the data for the note details page,
6871.14 -> we bring back another function which we saw before already,
6874.47 -> the loader function.
6877.011 -> Because that's the function you need
6878.25 -> to load data for a component on the backend,
6882.733 -> which is what we wanna do here.
6884.25 -> Again, Remix will execute this automatically
6886.68 -> when we try to load this page.
6888.48 -> And in here we now wanna reach out to our notes.json file
6892.98 -> and fetch the right note for this website here,
6897.309 -> for this page here, I mean.
6899.009 -> For this I use this getStoredNotes helper function,
6903.795 -> which is imported from data notes,
6906.072 -> so from this notes JS file here in the data folder.
6909.99 -> And as you learned before, this returns a promise.
6912.66 -> So I will use async here and then we get our notes.
6918.493 -> But here of course we get all notes.
6920.32 -> Now if this would be a real database,
6922.989 -> there would probably be a dedicated function
6925.488 -> for just fetching a single entry from the database.
6927.54 -> Here I'm instead fetching all entries from that file.
6931.849 -> And now I wanna find the selected note
6934.26 -> by going to my notes and I can use the find method,
6937.95 -> a default JavaScript method that exists on arrays
6942.06 -> to find the note where the ID is equal
6946.5 -> to the ID that's part of the URL here.
6951.008 -> But how do we get hold of that ID in that loader?
6954.36 -> Well, just like the action, the action function,
6958.837 -> this loader function also receives a data object
6964.173 -> passed in by Remix automatically.
6966.952 -> And this object also has a request property
6969.671 -> with details about the get request.
6972.06 -> But more importantly for us here,
6974.07 -> it also has a params property
6976.29 -> with information about the dynamic route parameters.
6980.671 -> So these dynamic path segments like this ID here.
6984.21 -> So basically if you have a dollar sign here
6987.094 -> in the route file name, it's the part after the dollar sign
6989.34 -> that's part of this params object.
6992.446 -> This placeholder can be used for accessing the actual value
6995.73 -> that's stored for this placeholder in the URL.
7000.493 -> By the way, this params object is also available
7002.3 -> in an action if you would need it there.
7005.734 -> Here we don't.
7006.733 -> But if you had an action that also runs for a page
7009.692 -> that has some dynamic path segment,
7011.24 -> you would get access to the params object here
7014.647 -> on actions as well.
7016.43 -> But here we need it for the loader.
7018.35 -> I'm using object destructuring
7021.13 -> for getting hold of this params object.
7022.79 -> And then here I know that my noteID can be found
7026.66 -> on params.noteID.
7029.869 -> And it's noteID here because I chose noteID here
7033.44 -> after the dollar sign.
7036.724 -> If you chose a different identifier after the dollar sign,
7040.79 -> you must use a different identifier here as well.
7044.96 -> For me, it's noteID though
7047.765 -> and therefore here in this check down there,
7049.22 -> I check whether I find a note which has an ID
7054.168 -> that is equal to that noteID that's part of my URL.
7057.971 -> And then I try to return that note
7059.731 -> and store it in this constant.
7061.4 -> Therefore then I can return this selected note in my loader.
7065.629 -> And in the note details page,
7068.57 -> we again get access to that data with UseLoaderData,
7073.155 -> which is imported from Remix run React.
7076.34 -> So here I get my note and now we can output the note title
7082.385 -> by accessing note.title, just like that,
7085.67 -> at the content by accessing note.content like that.
7095.367 -> And with that, if we reload,
7096.968 -> you see I have some concrete data here.
7099.948 -> If I click on the second note with Remix is awesome,
7102.8 -> I see that here as well.
7106.205 -> So that's how we can fetch dynamic data for dynamic routes
7109.068 -> that have dynamic values in the URL.
7113.277 -> Now when we're loading data for this dynamic route here,
7115.85 -> we could of course have the scenario
7118.55 -> that a user tries to access this page
7121.79 -> for a noteID that doesn't exist.
7124.58 -> In that case here, our default error handler becomes active.
7130.25 -> But we might wanna handle this on our own here
7133.825 -> because this is quite a common scenario.
7136.202 -> It's quite easy to imagine that we have this problem.
7139.16 -> Therefore here in the loader of this dynamic route,
7142.19 -> we can check if selected note is not defined,
7145.49 -> if it's faulty,
7147.02 -> which means we haven't found this note for this ID.
7151.31 -> And in that case we could throw an error response
7156.41 -> with help of that utility helper json function
7159.68 -> imported from Remix run note.
7162.907 -> And there we could add an object with a message of
7168.89 -> could not find note for ID
7173.36 -> and then maybe repeat that noteID
7176.172 -> which the user tried to access
7177.92 -> and give it a status code of 404 again.
7184.128 -> So now again, we're throwing an error response
7185.78 -> and as we learned before,
7187.532 -> this will trigger the catch boundary,
7190.247 -> either a catch boundary we add to this route,
7192.89 -> but if we don't do this then the root catch boundary.
7197.48 -> So here I won't add a dedicated catch boundary
7201.89 -> and instead I now get this default error handler,
7206.45 -> this default catch boundary from the root component.
7211.132 -> After all these crucial core features,
7213.77 -> finally, to conclude this section and to come to an end,
7217.948 -> there is one last essential feature
7219.14 -> you also should know about.
7222.306 -> And that would be metadata
7224.812 -> and how you can add metadata to your pages.
7227.969 -> But to be clear, what exactly do I mean by metadata?
7231.591 -> Well, I for example mean that title
7232.94 -> which shows up here in the tab bar
7235.52 -> or which also shows up in Google search results.
7238.85 -> And I also mean this description which shows up here
7242.81 -> and a bunch of other metadata
7244.108 -> you might wanna add to your pages.
7246.53 -> Adding such metadata to pages with Remix
7249.29 -> is again quite straightforward and simple.
7252.44 -> All you have to do, for example here in notes.jsx
7255.92 -> is export another new function.
7259.07 -> And by the way, it does not matter
7261.382 -> where in this file you export the function,
7263.84 -> the order does not matter.
7266.3 -> So here maybe after the links function,
7268.49 -> I'll export another function which is called meta.
7271.82 -> And you might have seen this function before
7274.22 -> in that route.jsx file there actually also
7277.98 -> is such a meta function already.
7281.02 -> Now meta is a function
7281.853 -> that should return a JavaScript object
7284.3 -> and on this object you can set various metadata fields.
7289.01 -> Now you learn a bit more about the available fields
7292.07 -> and how meta works and the official documentation,
7294.98 -> but you can for example,
7296.69 -> add the title field to set the title a page,
7300.956 -> the title that will show up
7302.417 -> as a title in your search results
7304.058 -> and also in the tab bar title, for example.
7307.01 -> And here that could, for example,
7309.084 -> be all notes for this notes page.
7311.564 -> We can also add a description here
7314.96 -> and that could be manage your notes with these,
7319.617 -> anything like this.
7320.66 -> Now the metadata you set up on a page
7323.18 -> will be merged with any higher level page metadata
7327.959 -> you might have to find.
7328.792 -> And for example, in the root page
7330.32 -> we also have a meta function
7332.21 -> and the data defined here, the metadata defined here
7336.343 -> would be merged with metadata
7338.38 -> defined in one of the other pages
7339.74 -> since they are wrapped by the root page
7343.441 -> as you learned before.
7345.656 -> If we have clashes like this title here,
7347.3 -> which is to find both in the metadata
7350.119 -> of notes.jsx and root JSX, the lower level page wins.
7354.92 -> So the more specific metadata definition wins.
7358.22 -> The metadata definition of this page here
7360.5 -> wins over the generic root metadata.
7364.55 -> And that is how you can add your metadata.
7367.37 -> If you, again save this, you will notice that
7370.34 -> for this all notes page, the title here now says all notes,
7377.24 -> instead of new Remix app, which it says on other pages.
7382.28 -> We can also add metadata by copying this function
7385.82 -> to keep things simple to this dynamic route here.
7391.04 -> And there we of course might want to access
7393.56 -> some dynamic data.
7396.274 -> For example, the title here should probably be the title
7399.297 -> of the note that's being displayed.
7400.88 -> This is again, pretty easy to do
7403.496 -> because like many other functions,
7404.955 -> the meta function automatically receives
7406.919 -> a data object provided by Remix.
7409.01 -> And that object, for example, has a data property.
7413.58 -> So now this is not the overall data object,
7415.884 -> but a property in that object
7417.921 -> that's provided by Remix that's called data.
7420.89 -> And this data property holds the data
7423.59 -> that is returned by your loader.
7425.9 -> So in this case, the note which we selected,
7429.18 -> this is what we receive here on this data property
7431.66 -> in this meta function.
7434.18 -> And therefore here we can of course set the title
7437.265 -> to data.title since we noted for this component,
7440.196 -> for this route, data will simply be a note
7442.94 -> because that is what our loader returns here.
7446.09 -> You could for example, also access params here
7449.143 -> if you would need access to the params themselves.
7450.89 -> But here I'm just interested in the final data.
7454.25 -> And with that, I'm setting the title off a specific note
7457.58 -> as a page title,
7459.47 -> and therefore if we visit one of these notes,
7463.49 -> you see that this page title is adjusted appropriately.
7468.316 -> Here, its Remix is awesome.
7469.49 -> And for the other note, it's new notes.
7474.53 -> And that's how you can add metadata to your pages.
7479.276 -> Now with all these features covered,
7480.74 -> you now know all these crucial features,
7485.073 -> the core concepts that make up Remix
7487.815 -> and that make Remix awesome.
7490.533 -> You can see how easy it is to build full stack applications,
7493.55 -> full stack websites,
7494.93 -> and blend backend and frontend with Remix.
7499.19 -> Now obviously Remix has way more to offer
7502.01 -> and we dive into more Remix features in the full course.
7505.61 -> We'll explore more features related to routing,
7509.108 -> to data fetching.
7509.941 -> We'll explore user authentication,
7511.7 -> which is actually also easy to implement with Remix.
7514.85 -> But here in this section,
7516.41 -> we learned about the core concepts you must know.
7519.71 -> You learned how routing generally works
7522.972 -> with these files in the special routes folder.
7525.2 -> You learned about dynamic routes.
7527.6 -> You learned about submitting data
7530.554 -> and handling data submissions with actions,
7532.82 -> how you can return responses for data from actions.
7536.84 -> You learned about data fetching with loaders
7540.076 -> and how that data is exposed to your components
7542.3 -> with UseLoaderData,
7544.49 -> or UseActionData in case of the action function
7548.81 -> and that you can use UseActionData and UseLoaderData
7552.47 -> in any component and it will get
7555.393 -> the closest action or loader data.
7557.57 -> You learned about this special form component
7559.64 -> and that you can update the UI
7561.53 -> based on the current data submissions status.
7565.43 -> You learned about error handling
7568.155 -> and how you can throw error responses
7569.93 -> to trigger a catch boundary
7571.64 -> or throw regular errors to trigger an ErrorBoundary.
7575.84 -> And that catch and error boundaries
7578.892 -> can be added to any route and or the root route
7581.75 -> as a lost resort catch or ErrorBoundary, so to say.
7586.19 -> You learned about styling and this special links function
7590.27 -> and how you can surface styles from components
7593.27 -> to the routes where those components are used.
7596.6 -> And you therefore now have all the core features you need
7600.332 -> to get started building your own first Remix applications.
7605.489 -> As mentioned, we'll dive much deeper into full course,
7607.52 -> but you now got the solid foundation you need