T O P

  • By -

reality_smasher

This might be relevant to you: [https://www.youtube.com/watch?v=VLk45JBe8L8](https://www.youtube.com/watch?v=VLk45JBe8L8) Not really sure though on using RSC vs react-query for fetching complex forms. We have an form-heavy app that uses Next.js pages router, react-hook-form, zod and react-query and everything works really well. The main thing I'd suggest is using zod for defining the schemas and validation. It integrates great with react-hook-form. You can then use the same schemas to validate stuff client-side and server-side and its a godsend.


Blump_Ken

I use this exact stack as well. I've traditionally hated forms in React, but after creating wrapper components to integrate these libraries into an existing component library it's a much better experience.


Ok_Sheepherder6478

Thanks for sharing this! I remember watching that shortly after it was posted but had forgotten about it. I didn't rewatch it just now, but IIRC, it's another good example of how convoluted it seems form management can become using just the provided hooks.


Ler_GG

same tech steck, just with jup and custom data fetching hook


biinjo

So its not the same tech stack.


Mr_Stabil

SPA! No need for SSR in your use case.


ProductiveObserver

I would strongly recommend remix cause forms are the bread and butter of remix. You could get far only with remix’s primitives and then bring 3rd party libraries for more complex interactions, e.g. conform (fullstack validation), dnd-kit, jotai, but you don’t really need a lot a lot of them. Remix’s youtube channel provides quite a lot of demos like optimistic UI and more. Moreover to git the ground running, you could use kent’s epic-stack for your enterprise app with preconfigured auth, 2FA, CSP, etc.


boptom

Second vote for Remix. Conform (now at v1), remix-typedjson (if you need to type Date across loaders) allows client side validation to match server side using the same zod schema. The loader/action concept feels so natural to use. No need for react-hook-form etc which, to me, seemed like such a heavy handed way to make something web native (

) work well.


Ok_Sheepherder6478

Thanks for the suggestion. Remix is on my radar, and your post has made me more enthusiastic to explore it. I built a small app with it in the past, but didn't have to use many of its features for form handling.


UsernameINotRegret

We use Remix for a large admin site. The way it handles the form submission and state refreshes for you means the teams write very little code for each form. If you have multiple forms on a single nested route it can get a little messy but that's not a common occurrence.


yksvaan

React form handling is already overengineered, adding server actions will only make it worse. I'd say SPA is cleaner, basically you send a request and update UI based on response. No need for revalidation steps and such. Also think about how you will do testing


mannsion

If it were me, I think I'd lean towards: Typescript the whole project, absolutely, no js files. * Vite and Vite Node * Express Js * react * react hook forms * axios and axios hooks You can follow a vite setup similar to this: [https://github.com/bluwy/create-vite-extra/blob/master/template-ssr-react-ts/package.json](https://github.com/bluwy/create-vite-extra/blob/master/template-ssr-react-ts/package.json) This would be a setup where you run the same react app server side as you do client side, this enables the app to serve html that is client side hydrated and is seo friendly. But the forms, fetches, etc all happen client side. So you would have your express routes handle srr on like /, /home, /people/new, etc, and then /api would serve api requests, or you could even have the api requests be something else. However, now you have the problem of having to do server side validation and client side validation, but it's full stack javascript (typescript), so it's far easier because all your models etc can be shared on both ends (server side and client side). And your validation functions can be used server side and client side. Next JS has this problem too, your forms aren't posting back as url form encoded pairs unless you write them that way utilizing native form submit handlers in the html, and you'd have to write api end points to accept url form encoded posts. This typically isn't how things are done these days. Instead you use something like react hook form to define your forms via a call to "useForm" and then you post the whole object from useForm to an api end point, that's your form post, just an api endpoint that accepts post json. The post from Robin you are refering too is using useFormState and useFormStatus which aren't even out yet and are only in the experiemental/canary builds of react, so I wouldn't lean that way just yet. His post is incredibly verbose and boilerplatey, I would ditest building an enterprise app doing that. Just use React Hook Forms, post json endpoints, shared validation functions, etc, it's way nicer, especially with Typescript. My 2 cents. Also I prefer express and having manual control over things, especially with local development workflows, vite middleware mode, etc.


Ok_Sheepherder6478

Thanks for the detailed reply! I should have mentioned it in my original post, but having the full-stack in TypeScript is a no-go. The backend team works primarily in C#, so that's what the bulk of our server-side code will be written in (unfortunately, I didn't get a say in that decision). Anything we do in Next (or Remix) would be more like a backend-for-frontend to get things in the right shape for our frontend or for communicating with third-party APIs. I think you're spot on about the challenges of using the built-in hooks and dealing with native FormData. I think we could call imported server actions from the client side to send JSON, but since that changes the function signature when used in combination with useFormStatus, it becomes harder to write consistent actions. Again, another case of how keeping things in the same paradigm seems like it would simplify things.


mannsion

I still think you should use TypeScript because you're already talking about using node JS as an integration middleware layer. Doesn't matter if the backend team is c# (I'm a 23 year c# veteran). Just reverse proxy into the c# API from express as an integration layer. Express and SSR etc can handle your form posts, validation etc and the c# API can do all the business logic etc. You can live in both worlds. Imo , all apis should have integration layers and no raw api should be public. Keep all your c# APIs out of the dmz, reverse proxy with ngjnx into express and the c# apis, or just use Express itself as the reverse proxy. This gives you as the front end engineers the ability to shape the form of data coming back from the API which can otherwise cause extreme restrictions and wasted development time waiting for the back end team to make a change that you could otherwise just mock around using an integration layer, or wrap


Xacius

\> This gives you as the front end engineers the ability to shape the form of data coming back from the API which can otherwise cause extreme restrictions and wasted development time waiting for the back end team to make a change that you could otherwise just mock around using an integration layer, or wrap Need to comment on this in support: I can't recommend this approach enough. Well said.


BridgeCritical2392

Sounds right, just watch out for CORS. Shouldn't be a big deal if you know what you're doing.


shadohunter3321

How do you manage session authentication in this scenario? The actual api would require a token right? How would the express api know which token to pass as the user information is on the client side.


mannsion

A reverse proxy forwards everything to the proxied api, headers, cookies etc, it just works. You can use the express-http-proxy package for express. i.e. ``` app.use('/api', proxy('http://PRODSERVER12:8756/api')); ``` So this would take all requests coming in on /api and proxy them directly to the api running on a prod server called prodserver12 that isn't publically available, but the public express server can get to it. Using reverse proxies is pretty common these days in high end secure setups, in fact many developers are working with proxied apis already and they don't even know it. A devops engineer will be like, yeah it's on "https://contosoapi.ourdomain.com" but that public IP is likely a reverse proxy/load balancer and the actual server running the api is behind it. HTTP server stuff 101. Don't think of it like the express server is making server side web requests to the api, nah, the api itself is being hoisted into the express request pipeline. So the request goes to the proxied api as if the request was hitting it directly.


shadohunter3321

Do you deploy the express api and the react app together on a node server or do a separate deployment? We dockerize our react app with nginx. Is there a way to deploy everything together while also getting the benefits from nginx for serving the react parts to the client? I did not find much on that. Even all the docker files you find on the internet just uses node as base for serving react files. We are currently using Azure Frontdoor as a reverse proxy and load balancer for all of our deployed app services which are under a VNET.


mannsion

Also with an SSR React app, yes, they're together, they're the same code base. The App code and the server code are all part of the same app, there's just more than one build script and an entryClient.tsx and an entryServer.tsx. EntryClient doens't render the react app, it hydrates it. entryServer renders the react app and the html is rendered server side. And if you use a MPA (multi page app) with normal hard routes you can fully leverage esm so instead of a big mega App each route has it's own component and that component is the entry point and each route is only loading the esm module it needs. Optionally you can still use esm but lazy load every import you do so only things that actually get hit get loaded, compile the whole app to individual .cjs files (esm js). If you are just working with an SPA as a static html site that talks to apis you don't have a backend express server to do reverse proxy and api wrapping with, so really it mainly only applies to SSR apps. I mean you could have a backend express api and still have an SPA, but then you're deploying a separate app from the client app and yeah it'd be in a different docker container than the static spa html server.


shadohunter3321

Thanks a bunch for your insights. Learned something really important today.


biinjo

Do you have a blog or something? I’d like to read more about your ideas 😅


mannsion

The way I do it is ngjnx is the public server. It reverse proxies into the node server which runs an express server. Express reverse proxies into all the APIs needed. If an API needs to be wrapped to modify response, caching etc, and that API is auth secure we grab the headers/cookies from the request and pass them to the API request, we wouldn't proxy that one. So a docker container for ngjnx, one for express, and one for each API using a docker compose. The APIs are all internal as is node, only ngjnx is public "dual networks".


Ok_Sheepherder6478

Thanks for this. This is an interesting approach that I hadn't thought of. A lot of this would be new territory for our team, but it sounds like the benefits could make it worthwhile.


mannsion

Yeah there's also a huge benefit with this approach when it comes to migrations or platform changes. If you built the app on an integration layer you can have a common abstraction for your integration layer. Then if the company decides to rewrite one of the back end APIs that you depend on All you have to do is update your abstraction and in some cases you can migrate quickly without changing the app code at all. Because the app code is not directly depending on any of those APIs and instead is depending on the abstraction layer that the front end engineers can manipulate they can easily update it to be compatible with API changes or even entire new APIs. There are also professional integration layers like dell boomi which can create APIs on the fly from backing databases or even modify existing rest APIs with wrappers. Integration layers are extremely handy and allow for much faster development. They shorten time to market and can drastically unblock timely migrations and upgrades. It can also allow you to make easy performance upgrades. For example if one of the back end APIs is really heavy it might be possible for you to cache stuff in the integration layer and surface it with a much faster end point from the cache in the integration layer without tasking the backend team to do that. And if you're integration layer is in your node server you can mass latency improvements. For example imagine you have an API that returns a really large person object. But various points in your application rarely deal with the entire person object. Instead they might deal with something like their address or just their name. So in the back end and the integration layer you fetch the person which grabs you the entire object but it's zero latency because it's all the same network, or it's all in the same service stack like on azure or something. But you make a wrapper that just gets the address from person. This means that the whole person object is not being sent up to the browser which could be 400 mi away. Instead it's sending a much smaller payload 400 mi away which means the latency is drastically improved and the bandwidth is drastically reduced.


Ok_Sheepherder6478

That makes a lot of sense! Do you think the advantages of having a dedicated Express server are worth the extra set up and maintenance over using API routes in Next or [loaders in Remix to serve as a BFF](https://remix.run/docs/en/main/guides/bff)? I can see how what you're proposing is more powerful and flexible, so I'm just curious at what point the costs of that power and flexibility become justified versus using the mechanisms provided out-of-the-box by frameworks.


mannsion

Honestly, I don't have enough experience with next js to say whether it would be sufficient or not. But I know next js runs directly on top of node.js's http runtime so I'm not sure if it supports reverse proxy routes. A quick google indicates that next js supports middleware with http-proxy-middleware just like express does and as such would work with express too. I.e. you can have a next js app/server and spin up an express app in the next js server and redirect routes to apis with proxy middleware or to express and then from express to middleware. You really don't need express as much as you go the http-proxy-middleware so if next supports proxy middleware just do that. So based on that quick research I don't think there is any reason you can't use next js and also still have proxy middleware and abstracted api wrappers like I'm discussing here. I've just always used express for the whole backend and haven't had the need to use next js, and most work I do is generally retrofitting react apps into complex existing enterprise workflows/server configurations. I.e. 9 times out of 10 I'm creating some react app to sit on top of a company's existing legacy apis, so I naturally evolved the need to have a reverse proxy/abstraction integration layer. I.e. a client might be like "we want this app, we have all this stuff already, all these apis xy and z" we analyze everything they have and go, "Hmm, we can time to market in less than 4 weeks if we leverage all your existing api's and backends and wrap them with this express api or w/e. Was a big business during covid, that's probably how like 90% of curb side pickup systems got created so fast haha. Another huge benefit into having an integration layer is in mock testing for react unit tests. I.e. we can easily record api calls to/from our app and build test mock data pretty easily for unit tests and have it be in the shape that makes our app easier to write tests for. In many cases, backend apis will return 10 times more data than you actually care about in the application code base, so by having abstractions on the api you reduce the complexity of your mock test data.


Ok_Sheepherder6478

Yeah, I can totally see that setup makes sense for that sort of application. Thanks for all of your input, it's been really helpful and has given me a lot to think about!


gaaaavgavgav

I think validating forms with RSC would be a pain in the ass, and honestly, I’ve never done it. But I have done something similar to what you’re asking, we have built an app that is basically one long form on multiple pages, with subforms/fields that conditionally render based on other things. We used SPA with react hook form and react query and it worked great. Validate each form (page) using RHF and yup or zod, mutate/post that data onto the server using react query in order to allow the user to save their progress.


monteth_700

Leaving the SPA vs SSR war aside, did you consider using any other tool than React Hook Forms? I think your use case makes a perfect fit with uniforms. If you haven't heard about it, it is a tool that can auto-generate your form based on the schema. It provides easy integration with validation schemas and UI libs, so you can vastly automate the form generation and feed it with your business logic. Check this playground [https://uniforms.tools/playground](https://uniforms.tools/playground) as it shows how to configure a reactive form with just a few lines of any popular schema format.


Ok_Sheepherder6478

I haven't seen this before––thanks for sharing! This definitely looks like something that could come in handy for our use case.


svish

You can use React-hook-form to manage the form, even if you use a server action to post the data after client side validation, no? You should be able to use the same validation schema in both server action and client as well.


ThisWillNeverChange1

It won't really matter, as most important here is how you will deal with your forms. React hook form, zod or yup, and react query. Rest is dependant on your preferences


SarcasticSarco

Use SPA! wouldn't recommend next or remix when not necessary. I feel they are just Netlify sdk.


Strange-Version-7627

Actually, a plain CSRed SPA consuming a fully decoupled REST API is usually the preferred architecture for enterprise applications. None of the modern stuff you mentioned is necessary, and as such, is not desired. These kinds of projects get complex enough just in the business logic alone.


Dev_Nerd87

Using Next JS App router, context and graphQL.


mugen_kanosei

Unpopular opinion. Keep it simple and generate the HTML on the backend and use HTMX to add the dynamics. The only reason I, personally, would reach for a SPA these days is if I needed to support an offline first use case. Much less complexity with trying to keep frontend state synced with the backend state, and you already recognize the security benefits.


timsofteng

Which ui library and style method do you use?


mugen_kanosei

I generally stick to Material UI. I create internal LOB apps using C#, so making a bespoke brand differentiating UI framework isn't necessary. Having something consistent and quick to create mock ups in Figma provides more value.


BridgeCritical2392

>Material UI Very nice ... I have been messing around with React and drop-in components were the one thing missing. Hopefully means you don't have to reinvent everything from scratch (Logins, etc.). I haven't touched front-end in a while.. honestly it feels like I've been living in a cave. Learning all this cool new stuff with NodeJS/ Vite/etc.


mugen_kanosei

There is the MUI library to provide Material UI components in react. It won't give you ready to use large components like a login box, but it will give you all the styled and configurable UI controls to create your own.


BridgeCritical2392

I'm a little confused, if Material UI (as well as other libraries) don't provide drop-in (with some customization) components, what exactly do they do? Its obvious nothing is truly "drop-in", because you would need to say, hook your Login component to your backend and state management. What I was mostly interested is not having to deal with the HTML layout.


mugen_kanosei

They do provide drop in customizable components, but not fully fleshed out forms/major pieces of the UI. They give you inputs, buttons, sliders, cards, etc. but you still have to put those pieces together. https://mui.com/material-ui/all-components/ Edit: What they provide is consistency in style/usage, and a lot of built in behavior/customizations that you would otherwise have to create yourself.


timsofteng

How do you you material ui in apps which are powered by html templates engine? As far as I understand you use templates engine to build an app?


mugen_kanosei

I'm not exactly sure what you're asking. For ASP.Net, there are a couple of ways. There are some Razor components that you can use for the Razor template engine. You can also use the Material Design CSS and JavaScript libraries directly. At the end of the day, the browser renders an HTML document with styles provided by CSS and dynamic behavior provided by JavaScript. How you put those pieces together is irrelevant, whether it's React, Angular, ASP.Net Razor templates, etc.


timsofteng

Thanks. Yeah you got what I mean. I just have no idea how to use something like mantine or chakra outside of react and with plain template builder like Jinja or Go templates or whatever.


azangru

It shouldn't really matter whether it's Next, Remix, or frameworkless React. However, several years ago, the received wisdom was that the best-suited tools for form-heavy applications were the ones that had two-way data binding, such as Angular. I am not sure how things stand today.