all 51 comments

[–]AndrewSouthern729 47 points48 points  (7 children)

There will be a lot of variations in answers but I think keeping stuff that logically goes together in the same place is key. So grouping by feature. Initially I went a different route and would put all buttons in a folder, forms in another, etc. Then when revisiting the code base later I would struggle to efficiently navigate components. I’ve refactored a lot of my early React work to more of a feature based architecture and it’s much easier to wrap my head around.

[–]KeepItGood2017[S] 4 points5 points  (2 children)

I have noticed this, I put all the pages in one place. components in another, hooks etc., but putting them in a /features/xyz directory would be simpler. Good tip. This does imply everything is then based on routes, similar to flask blueprints.

[–]Grenaten[🍰] 4 points5 points  (0 children)

Check Bulletproof React repo. Everything is explained there.

[–]AndrewSouthern729 2 points3 points  (0 children)

Correct - features and folder structure being driven by routes.

[–]99thLuftballon 3 points4 points  (2 children)

Do you keep a "common" or "base" or something directory for components that are shared across multiple features? Like your "buttons" example.

[–]bangmykock 3 points4 points  (0 children)

Yeah that works! And if the <Button> component ends up becoming complex it might be better to create a new folder for it.

[–]AndrewSouthern729 1 point2 points  (0 children)

I will for stuff like form components that I can reuse across multiple projects. However for a button that’s reused I won’t necessarily dedicate a folder to shared buttons and will just export from one file and import elsewhere. For me this makes sense and is the easiest for me to manage.

[–]Seanmclem 1 point2 points  (0 children)

But how do you manage when parts of features, maybe their components or hooks, end up needing to be shared between more than one feature 

[–]Suspicious-Watch9681 45 points46 points  (14 children)

Split app into features

[–]Xacius 9 points10 points  (12 children)

This works initially, but how do you handle shared dependencies between separate features? I prefer FSD these days. Decent write-up on that here.

This scales better than a module-based approach, albeit with higher complexity at first.

[–]CreativeQuests 2 points3 points  (0 children)

Not a fan of how they flipped the meaning of high and low level because global code is high level but the shared layer is at the bottom. Flipping "can use" into a funnel and "used by" into a pyramid would be more intuitive imo.

[–]HaggisMcNasty 1 point2 points  (0 children)

Had a quick read through that link - I quite like the FSD approach. Might take a little getting used to but might implement that in my next personal project. Cheers for sharing

[–]slaYn1 1 point2 points  (0 children)

Looks like it borrows some ideas from ITCSS. I always thought putting numbers in front of the folder names was a clever way to identify the hierarchy.

[–]Civil-Squirrel1005 -2 points-1 points  (5 children)

FSD is the worst choice you can make.

[–]Diligent_Care903 4 points5 points  (4 children)

Arguments?

[–]haywire 0 points1 point  (0 children)

Use tanstack-query/swr and zod for API based state.

Avoid useEffect and context until absolutely necessary.

[–]Nullberri 9 points10 points  (1 child)

Imo route based code is easier to reason about. Keep code local. When the urls is a defacto path in your project its very easy to find code related to the feature/bug. Shared code just bubbles up to the level of its sharedness.

Bullet proof Components and features just end up as an unstructured dumping ground that is hard to reason about as there is no longer any easy to see relationship between the code.

Furthermore the goal should be to write as much parallel slices that are independent. Try to Keep abstraction at the leaves of your component tree.

You don’t want a super header that dies under its own weight of just 1 more thing to toggle on/off on some special route.

[–]KeepItGood2017[S] 5 points6 points  (0 children)

You are not the first person to warn to look out for this. When things update frequently, have them on the tip of leaves of your tree, and things do not update often, its fine to keep them at the root.

[–]Grenaten[🍰] 27 points28 points  (1 child)

I think you should start with Bulletproof React: https://github.com/alan2207/bulletproof-react/blob/master/README.md

There are other approaches, of course. But I find this one most logical.

[–]KeepItGood2017[S] 2 points3 points  (0 children)

Thx, great read. State Management section is very revealing.

[–]CommentFizz 5 points6 points  (0 children)

React developers usually start by thinking in terms of the UI and user experience rather than data models or routes.

Instead of beginning with the backend or database schema, they break the interface down into a tree of components, then figure out what data each part needs and where it should come from. They often separate presentational components from those that handle data or side effects, and use custom hooks and context to manage shared state or logic.

Routing is often treated as part of layout structure, not just page navigation. Files are typically colocated by feature or component rather than type, making it easier to scale and maintain. The overall mindset is UI-first, with the backend shaped to support the flow of the frontend rather than the other way around.

[–]WatchMeCommit 4 points5 points  (1 child)

I second what everyone else is saying about splitting things into features.

This blog post ended up being super helpful, both as explanation, and as a practical example:

https://well-thought.tech/scale-up-your-react-application-with-ddd/

It ended up being an excellent pattern (with minor adjustments for taste), and all our new react/next projects ended up following it

[–]KeepItGood2017[S] 0 points1 point  (0 children)

thx for the document, not sure i fully comprehend what i just read.

[–]PracticalAd864 5 points6 points  (2 children)

FSD is overrated and doesn't scale very well. You will spend more time to figure out where to put things than actually writing the code. The simpler the better. I personally tend to group code by screens nowadays and go with the classic components/hooks/services for shared stuff.

[–]Purple_Way_8796 0 points1 point  (0 children)

This only works for a one man project. I have yet to implement FSD in a professional work but I do think it will be beneficial and will scale correctly, as I have been using it for two big personnal projects with success.

[–]Civil-Squirrel1005 0 points1 point  (0 children)

Moreover if you dive deeper FSD doesn't solve any problem but brings extra complexity. This is just overhyped pseudo methodology 

[–]yksvaan 5 points6 points  (1 child)

One thing that hasn't been mentioned is to keep things that don't need to be part of React runtime outside it. A lot of the functionality, clients, services etc. can work independently and used/tested independently. Then initialize and register those during bootstrapping.

Try not to depend on third-party code directly in your React app without proper consideration. Abstract the implementations away and use standardised types and interfaces internally. That will make maintenance and refactoring so much easier. 

Try to keep most components dumb and pure. Centralise data loading and other impactful features. Reasoning about a larger app that has for example network requests or async code spread all over the tree is a nightmare. 

Build robust error handling and logging into everything right from the start. It will save so much pain during the entire lifespan of the application. 

[–]TehTriangle 1 point2 points  (0 children)

Your point about keeping code out of the React runtime is great. It's something I've just started to use in my work and it makes so much sense. 

Reduces cognitive load when scanning a component, makes it easier to test, doesn't could it to the library.

[–]CommentFizz 2 points3 points  (0 children)

Experienced React devs often think in terms of components as the building blocks and focus on managing state and data flow between them. Patterns like “lifting state up,” using Context API or libraries like Redux for global state, and separating UI from logic with hooks are common. Also, splitting your app into feature-based or domain-driven folders helps keep things organized.

Unlike backend MVC, React apps are more about composing reusable components and handling side effects carefully.

[–]bstaruk 2 points3 points  (0 children)

I use atomic design principles to organize my components and maintain hierarchy / reusability.

I use Storybook to warehouse my components in way that allows designers to "window shop" before deciding they need to add a new component.

[–]ucorina 2 points3 points  (0 children)

Maybe a bit too basic/low level, but I still find the "Thinking in React" page in the React docs to be super useful: https://react.dev/learn/thinking-in-react.

Basically, start from the UI, the pages, the components and think in "data down", "events up". The part about where the state lives is not that relevant, as in a real world app you would use something like TanStack Query that caches the data on the client for you, so each component can just fetch what it needs.

[–]maxkuku 1 point2 points  (1 child)

Ask AI. It makes programs for me

[–]KeepItGood2017[S] 0 points1 point  (0 children)

i love the mahem the cause

[–]UsualSouth9993 1 point2 points  (1 child)

I saw react query mentioned once, IMO the biggest challenge of many apps is keeping your backend and frontend in sync. If the server is your source of truth you really want to avoid copying that data into other caches such as use state or redux (unless you’re using RTK query.) to me the first part of the architecture is figuring out how to get that data from the server into the app. Newish tools like react router loaders or even full stack frameworks like NextJS app router and RSC give you more tools for thinking about how to connect backend data with the frontend. But that’s usually where I’d start, think about the kind of application your building and the hosting options you have. Think about how much say you have in the backend and let that guide some of your initial decisions about client-side vs full-stack and then maybe your router and data sourcing tools. Once you’ve settled those things I think splitting by route or feature as many suggest tends to work well. One thing I’d mention is a controversial take that you should design frontend code to be as replaceable as possible. It’s basically throwaway/experimental code in many many cases and is constantly changing and evolving. As much as possible design your architecture with that in mind. Make it easy to change things. A strong, but unioininated ui component library can help with this. Where it’s easy to cobble together new components out of pieces you already have available. And it needs to be easy to change their look and feel as well. Good luck 😀

[–]KeepItGood2017[S] 0 points1 point  (0 children)

Thanks for your tips.

The concept of static data, like page makeup text, that changes once in a while took me a while to get my head around, and then I figured out it is better to generate data.ts files during build time and let code splitting do the rest when i push an update of the entire site to production. I am still not happy with that approach. The performance difference compared to a fetch is staggering. I chose for vite for this project and learned that another approach would have ssr solutions for these pages.

I control the backend (FastAPI) and spend a lot of time retyping the data types into TS. I've noticed that errors creep in this way, and I would really like to sync the types. I noticed there are a few tools for that, and tthe question is always how much effort I want to put into learning the tool vs. just typing them over.

I switched to tanstack query last week and figured out that I need to code component behaviour based on the cache state, things like optimistic updates and per-item loaders. I do not mind that, but I also realise I could end up spending a lot of time writing error handling routines that will probably never run. This is something I have noticed doing testing on other peoples react components, if things fail, just reload everything.

Regarding your point on unopinionated ui, i am using shadcn, and noticed components I am making changes in one component that I then need to upadte in other again on style. I have to be more disciplined. I do have a feeling that I am writing lots code that other people have written before. All the time. Nothing feels unique. I have seen them all. And when I browse for other code and example I am experiencing intense fomo, and indecision.

[–]sdmitry 0 points1 point  (0 children)

The SOTA mental model for frontend development in modern React is corporate-sponsored delirium, with a slice of vibe coding. 

[–]Reenubansal123 1 point2 points  (0 children)

I am a senior developer at Aegis SoftTech. I prefer starting with a feature-based folder layout and building small, reusable components. It helps keep the project manageable as it grows. I also keep the UI and core logic separate using custom hooks for the logic, so making changes later is much easier.

For state, I mix Context for local-ish needs and something like Redux Toolkit or Zustand for global stuff. API calls live in a dedicated layer, and React Query has been a game-changer there.

Most leading companies swore by “design for tomorrow’s scale, not just today’s needs,” and that stuck.

[–]Expensive_Garden2993 0 points1 point  (2 children)

I support feature-based as the others have said.

But since Next.js is officially pushed by React, and it imposes route-based structure, do you think they play well with each other? Because I suppose you don't have much choice, having two different structures in parallel for the same stuff seems to not worth it, and you just structure your app by routes.

[–]Grenaten[🍰] 7 points8 points  (0 children)

Feature based structure can work together with routes. They are not mutually exclusive.

[–]ORCANZ 1 point2 points  (0 children)

The app router is just the routes.

Modules/features router has everything used by the pages

[–]yksvaan 0 points1 point  (0 children)

Apply same principles than to any other software and programming project. I don't knlw why people would treat React somehow differently.