{"_path":"/blog/next_js_vs_remix","_draft":false,"_partial":false,"_empty":false,"title":"Next.js vs. Remix.","description":"Nowadays, a new React framework is no news. It seems like they are multiplying each day, so why are we focusing on Remix and Next.js today?","excerpt":{"type":"root","children":[{"type":"element","tag":"h1","props":{"id":"nextjs-vs-remix"},"children":[{"type":"text","value":"Next.js vs. Remix"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"Nowadays, a new React framework is no news. It seems like they are multiplying each day, so why are we focusing on Remix and Next.js today? Well, Next.js is definitely the most popular choice for building SSR and SSG React applications, and Remix might be the next developer's favorite React framework. They have a lot in common and a lot more differences."}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"In this article we will talk about:"}]},{"type":"element","tag":"ul","props":{},"children":[{"type":"element","tag":"li","props":{},"children":[{"type":"element","tag":"a","props":{"href":"#story-of-the-two"},"children":[{"type":"text","value":"Story of the two frameworks"}]},{"type":"element","tag":"ul","props":{},"children":[{"type":"element","tag":"li","props":{},"children":[{"type":"element","tag":"a","props":{"href":"#nextjs"},"children":[{"type":"text","value":"Next.js"}]}]},{"type":"element","tag":"li","props":{},"children":[{"type":"element","tag":"a","props":{"href":"#remix"},"children":[{"type":"text","value":"Remix"}]}]}]}]},{"type":"element","tag":"li","props":{},"children":[{"type":"element","tag":"a","props":{"href":"#comparison-of-remix-and-nextjs"},"children":[{"type":"text","value":"Comparison of Next.js and Remix"}]},{"type":"element","tag":"ul","props":{},"children":[{"type":"element","tag":"li","props":{},"children":[{"type":"element","tag":"a","props":{"href":"#routing"},"children":[{"type":"text","value":"Routing"}]},{"type":"element","tag":"ul","props":{},"children":[{"type":"element","tag":"li","props":{},"children":[{"type":"element","tag":"a","props":{"href":"#nested-routes"},"children":[{"type":"text","value":"Nested Routes"}]}]},{"type":"element","tag":"li","props":{},"children":[{"type":"element","tag":"a","props":{"href":"#dynamic-routes"},"children":[{"type":"text","value":"Dynamic Routes"}]}]}]}]},{"type":"element","tag":"li","props":{},"children":[{"type":"element","tag":"a","props":{"href":"#data-loading"},"children":[{"type":"text","value":"Data Loading"}]}]},{"type":"element","tag":"li","props":{},"children":[{"type":"element","tag":"a","props":{"href":"#sessions-and-cookies"},"children":[{"type":"text","value":"Sessions and Cookies"}]}]},{"type":"element","tag":"li","props":{},"children":[{"type":"element","tag":"a","props":{"href":"#working-with-forms"},"children":[{"type":"text","value":"Working with Forms"}]}]},{"type":"element","tag":"li","props":{},"children":[{"type":"element","tag":"a","props":{"href":"#styling"},"children":[{"type":"text","value":"Styling"}]}]},{"type":"element","tag":"li","props":{},"children":[{"type":"element","tag":"a","props":{"href":"#seo"},"children":[{"type":"text","value":"SEO"}]}]},{"type":"element","tag":"li","props":{},"children":[{"type":"element","tag":"a","props":{"href":"#other-comparisons"},"children":[{"type":"text","value":"Other Comparisons"}]}]}]}]},{"type":"element","tag":"li","props":{},"children":[{"type":"element","tag":"a","props":{"href":"#conclusion"},"children":[{"type":"text","value":"Conclusion"}]}]}]},{"type":"element","tag":"h2","props":{"id":"story-of-the-two"},"children":[{"type":"text","value":"Story of the two"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"To better understand the competition between the two, let's first look into their story."}]},{"type":"element","tag":"h3","props":{"id":"nextjs"},"children":[{"type":"text","value":"Next.js"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"Next.js was first released as an open-source project on GitHub on October 25, 2016. It is a project of the Vercel (ex-ZEIT) company. Google has donated to the Next.js project, contributing 43 pull requests in 2019, where they helped in pruning unused JavaScript, reducing the loading time, and adding improved metrics. As of March 2020, the framework is used by many large websites, including Netflix, GitHub, Uber, Ticketmaster, and Starbucks. As of now, 12 major versions of the framework were released. Next.js has 86.5k stars on GitHub and 2,541,419 weekly downloads."}]},{"type":"element","tag":"h3","props":{"id":"remix"},"children":[{"type":"text","value":"Remix"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"Remix is a framework which was made by the same people, who made React Router. Remix's earliest commits on GitHub are dated to early July of 2020. It wasn't open-source from the start, but the strategy changed in late 2021 when Remix became fully open-source. Even though Remix is a relatively new framework, because of its core principles and ideas, it showed great growth in popularity. Remix has 16.2k likes on GitHub and 20,858 weekly downloads."}]},{"type":"element","tag":"h1","props":{"id":"comparison-of-remix-and-nextjs"},"children":[{"type":"text","value":"Comparison of Remix and Next.js"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"Remix and Next.js are both React frameworks, that allow you to do server-side rendering, which means that both backend and frontend are implemented in the same application. Render happens on the side of a server and the pages are sent to the client with minimal use of JavaScript. However, by the time of writing, Remix doesn't support static site generation."}]},{"type":"element","tag":"h2","props":{"id":"routing"},"children":[{"type":"text","value":"Routing"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"The routing system might seem similar at the first glance, but it isn't. What's similar is the file-based routing, where every file in a specific folder is treated as a new route. This specific folder is routes in Remix and pages in Next.js."}]},{"type":"element","tag":"h4","props":{"id":"nextjs-1"},"children":[{"type":"text","value":"Next.js"}]},{"type":"element","tag":"code","props":{"code":"- pages\n - index.js\n - new-route.js\n - _app.js\n ...\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"- pages\n - index.js\n - new-route.js\n - _app.js\n ...\n"}]}]}]},{"type":"element","tag":"h4","props":{"id":"remix-1"},"children":[{"type":"text","value":"Remix"}]},{"type":"element","tag":"code","props":{"code":"- app\n - routes\n - index.js\n - new-route.js\n ...\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"- app\n - routes\n - index.js\n - new-route.js\n ...\n"}]}]}]},{"type":"element","tag":"h3","props":{"id":"nested-routes"},"children":[{"type":"text","value":"Nested Routes"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"Because Remix is built on top of React Router, Remix allows you to do nested routing with ease."}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"It comes with a very powerful route nesting mechanism that can nest other routes in a currently active route to create what would be considered a nested layout of routes. They act like children components that can be mounted or unmounted depending on the currently active URL path."}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"This nesting is achievable using an Outlet component. The Outlet component is the key to nested routing in Remix as it is used to tell a parent route where to mount a nested child route. Using the Outlet component, you could easily build a hierarchy of deeply nested routes to create a complex nested layout. Through nested routes, Remix can eliminate nearly every loading state as these nested routes are preloaded on the server, creating almost a hybrid of SPA and SSR."}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"Next.js comes with its own router and has support for nested routes but it’s not as easy to achieve a nested routes layout as in Remix."}]},{"type":"element","tag":"h3","props":{"id":"dynamic-routes"},"children":[{"type":"text","value":"Dynamic Routes"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"Both Remix and Next.js have support for dynamic routing but have different naming conventions to create one."}]},{"type":"element","tag":"h4","props":{"id":"nextjs-2"},"children":[{"type":"text","value":"Next.js"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"In Next.js it’s created using pair of square brackets with the dynamic param’s name inside."}]},{"type":"element","tag":"code","props":{"code":"pages/post/[postid].js\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"pages/post/[postid].js\n"}]}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"Then using the "},{"type":"element","tag":"code-inline","props":{},"children":[{"type":"text","value":"useRouter"}]},{"type":"text","value":" hook provided by Next.js, the URL query param can be gotten."}]},{"type":"element","tag":"h4","props":{"id":"remix-2"},"children":[{"type":"text","value":"Remix"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"In Remix, to create a dynamic route you should start the name of the dynamic route with a dollar sign."}]},{"type":"element","tag":"code","props":{"code":"routes/post/$postid.jsx\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"routes/post/$postid.jsx\n"}]}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"And because Remix is based on React Router, the "},{"type":"element","tag":"code-inline","props":{},"children":[{"type":"text","value":"useParam"}]},{"type":"text","value":" hook can be used to access the URL param."}]},{"type":"element","tag":"h2","props":{"id":"data-loading"},"children":[{"type":"text","value":"Data Loading"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"We will only talk about SSR here, because, as said above, Remix doesn't support server-side generation (SSG) option."}]},{"type":"element","tag":"h4","props":{"id":"nextjs-3"},"children":[{"type":"text","value":"Next.js"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"In Next.js we have a "},{"type":"element","tag":"code-inline","props":{},"children":[{"type":"text","value":"getServerSideProps"}]},{"type":"text","value":" function, which does the job of loading data on a server, sending it to a React component and rendering it with the data. This data is sent via props."}]},{"type":"element","tag":"code","props":{"code":"export const getServerSideProps = async ({ params, query }) => { \n // abstract API calls\n const userData = await API.getUser(params.id);\n const { id, name } = userData;\n return {props: { id, name }}\n};\n\nexport default function UserPage(props) { \n return ( \n <> \n

User ID: {props.id}

\n

User Name: {props.name}

\n \n );\n}\n","language":"js"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"export const getServerSideProps = async ({ params, query }) => { \n // abstract API calls\n const userData = await API.getUser(params.id);\n const { id, name } = userData;\n return {props: { id, name }}\n};\n\nexport default function UserPage(props) { \n return ( \n <> \n

User ID: {props.id}

\n

User Name: {props.name}

\n \n );\n}\n"}]}]}]},{"type":"element","tag":"h4","props":{"id":"remix-3"},"children":[{"type":"text","value":"Remix"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"In Remix, however, we have a "},{"type":"element","tag":"code-inline","props":{},"children":[{"type":"text","value":"useLoaderData"}]},{"type":"text","value":" hook and "},{"type":"element","tag":"code-inline","props":{},"children":[{"type":"text","value":"loader"}]},{"type":"text","value":" function, which do the job for us and don't mix client props with server props. Loader function is an equivalent to Next.js "},{"type":"element","tag":"code-inline","props":{},"children":[{"type":"text","value":"getServerSideProps"}]},{"type":"text","value":"."}]},{"type":"element","tag":"code","props":{"code":"import { useLoaderData } from \"remix\"; \n\nexport const loader = async ({ params, request }) => { \n // abstract API calls\n const userData = await API.getUser(params.id);\n const { id, name } = userData;\n return { id, name };\n}; \n\nexport default function FirstPage() { \n const { id, name } = useLoaderData();\n return ( \n <> \n

User ID: {id}

\n

User Name: {name}

\n \n );\n}\n","language":"js"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"import { useLoaderData } from \"remix\"; \n\nexport const loader = async ({ params, request }) => { \n // abstract API calls\n const userData = await API.getUser(params.id);\n const { id, name } = userData;\n return { id, name };\n}; \n\nexport default function FirstPage() { \n const { id, name } = useLoaderData();\n return ( \n <> \n

User ID: {id}

\n

User Name: {name}

\n \n );\n}\n"}]}]}]},{"type":"element","tag":"h2","props":{"id":"sessions-and-cookies"},"children":[{"type":"text","value":"Sessions and Cookies"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"There are no built-in tools to interact with cookies or sessions in Next.js. You can use a library for that like Cookie.js or NextAuth.js, which would solve your problem."}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"In Remix, you get full support for cookies and sessions out of the box. You can generate a cookie by calling a function, then serialize or parse data to store or read it."}]},{"type":"element","tag":"code","props":{"code":"import { createCookie } from \"remix\";\n\nconst cookie = createCookie(\"user\", {\n expires: new Date(Date.now() + 60),\n httpOnly: true,\n maxAge: 60,\n path: \"/\",\n sameSite: \"lax\",\n secrets: [\"1s2e3c4r5e6t\"],\n secure: true\n});\n","language":"js"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"import { createCookie } from \"remix\";\n\nconst cookie = createCookie(\"user\", {\n expires: new Date(Date.now() + 60),\n httpOnly: true,\n maxAge: 60,\n path: \"/\",\n sameSite: \"lax\",\n secrets: [\"1s2e3c4r5e6t\"],\n secure: true\n});\n"}]}]}]},{"type":"element","tag":"h2","props":{"id":"working-with-forms"},"children":[{"type":"text","value":"Working with Forms"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"Working with forms is a pain in React. And Next.js is not much different from React when it comes to forms. You would need to implement everything yourself or use a library."}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"Remix's approach is different and took a lot from the way browsers handle forms natively. All you need to get a form working in Remix is to either use the traditional HTML form tag or import a Form component (both work the same) and set up a form with an HTTP request method set to POST or GET, then enter an action URL to send the data to - which by default will be the same as the route for the form. If the method is set to GET, Remix will execute any export loader function defined in the component, but if the method is POST, Remix will execute any export action function defined in the component."}]},{"type":"element","tag":"code","props":{"code":"export const action = async ({ request }) => {\n const form = await request.formData();\n const content = form.get('description');\n return redirect('/');\n}\n\nexport default function DescriptionForm() {\n return (\n
\n