1. Introduction to Remix and Modern Web Development
Hello, I'm Alexandra Spallato. I'm a Developer Relations Engineer at Storyblock. I'm super happy to be here.
My topic today is how Remix is embracing web standards to redefine modern web development. So first, let's dive into the evolution of web development. Web development is all about transforming data into HTML and delivering it to the browser for rendering.
All right. So now let's talk about the elephant in the room. The challenges we face in modern web development. Scalability. Your app always needs more users, more data, more complexity without crashing. New frameworks are popping up every week. It's like a fashion trend. So keeping up with the lastest and the greatest can be exhausting and time consuming. Performance. Nobody likes a slow website. Browser compatibility. We need our apps to work seamlessly across all browsers. UI and UX. This is all about our users. Developer experience.
2. Introduction to Remix
This is about us developers. We want our tools to be powerful, but also to be intuitive and enjoyable to use. Security. We need to protect our users' data.
3. Remix: Streamlining Development with Web Standards
Remix streamlines development with web standards, including URLs, fetch API, HTML, and HTTP caching. URLs are the backbone of the web and Remix uses them for routing and data loading. HTML is the language of the web, and Remix utilizes it fully. HTTP caching improves web app performance. Remix treats URLs as a powerful tool, optimizing navigation and streamlining development. The fetch API in Remix is like a personal postman, delivering data to your app. It manages requests, responses, and headers.
So now let's dive into how Remix streamlines our development process with web standards. First off, Remix loves URLs. URLs are the backbone of the web. The address of every little piece of content on the internet. Remix embraces this and uses URLs to manage routing and data loading.
Next up, we have the fetch API. This is our tool for data handling. And now let's talk about HTML. It's a language of the web. It's been around since the dawn of the internet. Remix use HTML to its full potential.
And last but not least, we have HTTP caching. By leveraging the browser's built-in caching mechanisms, Remix can drastically improve the performance of your web application.
So let's talk about URLs. In Remix, they are kind of a big deal. They're not just addresses, they're the backbone of your web architecture. So imagine a URL as a path. And each segment of that path corresponds to a nested layout in your UI. In Remix, you can co-locate your UI and data-handling code in the same file. We're talking about full-stack components. Remix also optimizes your client-side navigation. It knows which layouts will persist between two URLs. So it only fetches the data for the ones that are changing. And on top of it, Remix can prefetch all resources for a page when the user is about to click a link. So next time you look at a URL, remember, in Remix it's not just an address. It's a powerful tool that shapes your web architecture, optimizes your navigation, and streamlines your development process.
So now let's dive into the fetch API in Remix. It's like your personal postman, delivering data from the web to your app. It manages request and responses objects, your outgoing and incoming data message. We also have headers which are similar to the address in an envelope.
4. Data Handling and Cache Control in Remix
The fetch method in Remix allows you to make requests and receive responses from anywhere in your app. Loaders fetch data for components, providing the backbone of your app's data flow. Remix also offers helper methods like gson and Redirect to streamline working with the Fetch API. Remix simplifies data handling through HTML forms and HTTP, ensuring a smoother user experience. Httpication in Remix improves performance by reducing server load and speeding up response time. Cache Control headers in Remix allow for efficient data updates.
They carry additional information about the request or response, like the destination or the type of data being sent. The fetch method takes your request, delivers your response, and can be called from anywhere in your app. Loaders in Remix are functions that fetch data from the server for your components. There is a backbone of your app's data flow. And Remix adds requests, response headers, and fetch to your loader's global context. This means you can use them anywhere in your app, just like you would in the browser.
And lastly, we have helper methods like gson and Redirect that streamline working with the Fetch API. So with the Fetch API in Remix, handling data is as easy as sending a letter. So now let's dive into data rights and mutation in Remix. The magic is based on two basic web stuff. HTML forms and HTTP. Again, Remix is taking us back to the basics, but with a modern twist. When we talk about creating, deleting, or updating data on our website, we're really talking about form submissions. And this is not just about your big complex form like the user profile, edit profile page. Even the little like buttons are forms in disguise.
The Remix form components have some special features to the usual HTML form. After a form submission, all the loader on the page automatically refresh, showing users the latest info without needing to refresh the page. Reform your page, doesn't reload after a form submission, providing a smoother user experience. And then we can use hooks like use navigation that give us access to the pending form state, so we can build the user experience as smooth as butter. Think loading indicators, Optimix TQI, and all that good stuff. So in REMIX, action functions look after form submissions, and loader functions take care of getting data.
Httpication is a powerful feature that can really improve the performance of your web application. It's like giving your app memory. Instead of constantly asking the server for data, your app can remember some of it, reducing the load on your server, and speeding up response time for your users. In Remix, Httpication is used to its full potential. But what about updates? Well, Httpication in Remix is smart. It knows when to update the data. And this is where Cache Control comes in. You can define Cache Control headers right in your root modules. For example, you can set maxH300 to tell the browser to keep the cached data for 300 seconds.
5. Httpication, Global Search, and Storyblock
With Httpication, your web app becomes faster, your server gets less stress, and your users stay happy. The headers function exports an object with a cache control property, specifying the maximum amount of time the fetched response can be cached. Building a global search with Remix is simple and not tedious. You can access the tutorial and the project repository to rebuild it. The project includes a website with multiple pages, a blog page with posts, and a search feature. The search query is passed in the URL, and the results are filtered by page and post. The project is built with Storyblock, a visual editor for headless CMS.
So, with Httpication, your web app becomes faster, your server gets less stress, and your users stay happy. It's a win-win-win situation.
In this example, the headers function is exporting an object with a cache control property. The value of this property is a string that tells the browser how to cache the response. The maxH directive specifies the maximum amount of time in seconds that the fetched response is allowed to be cached. In this case, it's set to 300 seconds or five minutes. So, this code is essentially telling the browser, hey, you can store this reference and use it up to five minutes without checking back with the server. And also, you can find many of the directives in MDN docs as, again, this is web standard.
So now, it's time to code and to build into a project and to drink some water. So I don't know about you, but building a global search, for me, has always been super tedious. And when I discover a mix and I build that for my blog, I was amazed by how simple it was. So I created a tutorial about it. So you can get here with this QR code. You can grab this tutorial and you will have access to the repo and to everything to rebuild the project that we are going to build.
So let's explore this project. So this is the little website I built for that. So there is two pages, a home and an about page. And a blog page with some posts. Now if I open a post I have a headline, an image and text. And here I have this search button that opens a model window with the search form. And here I type my query. So I type remix. I hit enter. So we have, we see briefly this search word and we can see that our query now is passed in the URL and it displays the results, a filter by page and post. Okay. So this is a report. So you have it in the tutorial, but you can see there. And this is the tutorial. Okay. Let's dive into the code and yes, and this is built with Storyblock, my favorite headless CMS, which has a visual editor.
6. Search Form and UI Overlay
In our search form, we have a form component that behaves like a normal form, but with a fetch interaction instead of a new document request. We can simplify the form by using the default GET method and setting the action attribute to the search result route. This will process the form in the URL and attach the query value to it. Additionally, we have an overlay in the index that displays a searching word for a better UI experience during slow network connections.
So we have here's the pages. We can modify it here. And here is the blog. And in my page, I just have a body field. So this is where we are going to search if our query is contained. And in our blog posts, we have a headline. We have a headline, an image and a text field. So we need to search if our query is contained in our headline or in our text field or in our body field for our page.
Okay, so this is the code. We have this search result file and this is the route where we will find our results. We have our search form, which is placed into the index, into a model, which is built with Atlas UI, and then all that is placed into our header here. And yes, the results are displayed in the search results page. So, let's dive in now and explore our search form. We have our form here, the form component and here it says that it behaves like a normal form, except that the interaction with the server is with fetch instead of new document request. And so, this is why our page is not reloading when we submit a search. But, who remembers how a normal form behaves? It seems that I was not remembering because this is what I was doing. I was using the post method and then I was using an action in my root file here. Okay, so this action is here and from there I was getting the form data from the request and redirecting to the search result and passing the query to the URL. But, in fact, what I was doing here the basic form does it naturally. We don't need the POST method because the POST the form has a GET method by default but we need an action and this is action we need to set up the search result root and that's all what we need. So just with that our form is going to process in the URL defined in the action attribute, and then it will attach the value from our form, so our query, to the URL. So I was doing something, I was overcomplicating something that the basic form does naturally.
Okay, so let's continue. Then I have this overlay that is opening to have a better UI when we have a slow network and displays a word searching. So for that in my index, I have defined an open overlay and set open overlay state. And when I have open overlay, I show here my searching word. And here I'm using not in the header, I'm using the user, where is it? Sorry, I'm a bit lost. No, this is on the search form, and okay. I go back to the search form. So I have the use navigation hook that I initiate there with use navigation.
7. Handling Navigation State and Extracting Query
If the navigation state is not handled, the overlay is opened, indicating that the form is searching. When the navigation state is loading, the model is also closed. The code has been updated to simplify the form and utilize web standards. The useNavigation hook handles the navigation state, eliminating the need for additional state management. The search result page extracts the query from the URL using the URL constructor and utilizes the Storyblock content delivery API to filter the story.
And then if the navigation state is not handle, I open my overlay. It means that my form is searching. And if not, I close the overlay. And then when the navigation state is loading, I also close my model. Okay, so let's see how it works in the website. So here I have a slow 3G. Okay? And now I go to the console. And I open, so you see navigation state. I type remix. I enter, okay, and I see that it's loading. Okay, so we have an error here, so I think I have made a mistake with my action. Anyway, what I wanted to show you, it's also a bug because I was over complicating things and I was feeling something was not right. And a few weeks ago, I was giving this talk at React Rally in Salt Lake City, and I crossed Ryan Florence, the creator of Remix, and he told me what I was doing wrong. So now, I'm going to show you all the right code, and I wanted to show you that because of our React instinct, we are over complicating things. Okay, so let's go to the right branch. Okay, I have some changes. Okay, so I've made a new form. Okay, so here we have the form done the right way. You know, if oh, yes, I forgot the S at search results. That's why it was bugging. Okay, so action search results, and we have no method or anything, just that, and here I just need my use navigation, and I initiate it here, and then I create a searching variable, so it's a navigation set, and navigation location path name is search result. It means my form is searching. So I create a user effect, and so if yes, if it's searching here, I show my overlay, and then in user effect, if it's not searching, I also close my model window with the search. So what I want to show you is we have to unlearn all the things that we want to put the state everywhere because of this react instinct, and we don't need it, because we are using web standards and this use navigation is already doing everything. Okay, so now we are on the search result page, and we are going to go back to the slides. We need to extract the query from the URL. So let's go back to my slides, okay.
Okay, so to extract the query from the URL, we need to extract the query from the URL, and then to filter the story, for that we will use the Storyblock content delivery API, which is the REST API from Storyblock. So to extract the query, we use the URL constructor to create an object representing the URL defined by the constructors. Then this object includes a search property, which provides utility methods to work with the query string of the URL.
8. Fetching and Displaying Filtered Stories
To fetch our stories, we use the getStoryblock API from the React SDK and filter them using the filter query parameter. We search within specific fields using the like operation and retrieve all fields containing our query. By passing the filter parameter in our request, we can display the filtered stories. Using web standards in Remix improves app performance and efficiency, enhancing the user experience. We stick to web standards, ensuring our app is robust and future-proof. Check the Remix documentation and scan the QR code for a chance to win an Egghead subscription.
And now we need to fetch our stories, the stories in Storyblock are all the content items, so the pages, the post, and for that we use the getStoryblock API from the React SDK. So this will return all the stories, but here they are not filtered, so we need to filter them, and for that we use the filter query parameter to filter the story by specific attributes. So we are going to search within the body field for the pages, the headline and text field for the pages, and we use the like operation to check if a custom attribute has a value that is similar to our query. And we use an asterisk before and after, because we want to retrieve all the fields that contain our query. So then by default, everything is connected by the end operator, so we want the or operator for that we use the dash dash or keyword. OK, so this is all the code from our loader, so this is in the search result route, and we pass this filter parameter in our request here to retrieve the filtered stories. So this will return all the filtered stories. And now we just need to display them. So for that, we retrieve them using used other data, which is a remix of the hook that gets the data from our loaders. And then we filter our stories by component to separate the pages and posts. And then we have all the data to display that in our page and voila.
So, I invite you to check the Redmix documentation which is really good. And also, if you want to win an Egghead subscription, you can scan this QR code, as Storyblock is organizing a raffle, so scan the code and fill out the form by December 31. So, thank you. Here you can scan this QR code and get all the slides. Have a nice day and see you around.