The benefits, I can see.
JS frameworks move really quickly, and when we're working on a large, long-term project, it sucks when big breaking changes are introduced after only a couple of years. Sticking to slow-moving web standards (which are quite mature by now) increases the longevity of a project.
And the stability also means that more time is spent on delivering features, rather than on fixing compatibility issues.
There is also the benefit of independence. The project's success is not tied to the framework's success. And it also makes the project more secure, from supply chain attacks and such.
Because there is no "abstraction layer" of a framework, you also have greater control over your project, and can make performance optimizations at a lower level.
I feel not using a framework can even make us a better developer. Because we know more of what's going on.
There are benefits to using frameworks too, I'm not here to challenge that.But this alternative of using none... it seems rarely talked about. I want to learn more about building large (preferably web-based) software projects with few dependencies.
Do you have any suggestions on how to learn more about it? Are there any open source projects you know which are built this way? It needs to be large, complex, app-like, and browser based. I'm more interested in the frontend side.
Thank you!
React is a lot more stable than I think you're giving it credit for.
> And the stability also means that more time is spent on delivering features
Frameworks/libs also exist to save you time, thus letting you spend more time on delivering features. And fwiw, the obsidian team seems to agree in principle. Your link goes to a forum post of some kind, in which one may find a link to obsidian's third party deps: https://help.obsidian.md/credits#Third+party+acknowledgement...
These do not include React, but do include:
- i18next - lezer - moment.js
Plus a bunch of others. Why didn't obsidian write their own date lib and chose to use moment.js? Because it saved them time, despite the fact that moment.js does make changes, and many people are moving on from it in any case.
The idea that not using a frontend framework will let you focus on delivering features seems reductive, and the obsidian anecdote doesn't support the idea anyway.
Whatever you're building, it's never a bad idea to deeply understand the tradeoffs that using a library will bring. Obsidian probably couldn't accept the downsides of React due to them needing a bunch of custom renderers for content, which React makes harder. But that is likely a rare constraint for apps in general.
Generally speaking, libs like react exist to save you time and help you focus on delivering features.
Hooks are only 5 years old. The docs were revamped 2 years ago and there's lots of dead links to the old docs page which has a scary warning "These docs are old and won’t be updated." Create-react-app was deprecated in February of this year and in their blog post they tell you to use frameworks like Next.js.
And then there's the ecosystem. Next.js introduced app router 3 years ago and lots of docs for libraries still assume you're using pages router. Remix is now react router v7, and I have no idea what's going on with all this Tanstack stuff. There's a new typescript compiler called "Speedy Web Compiler" which just came out in April and as a result Vite now has 4 options for creating a new React project: react, react-ts, react-swc, react-swc-ts
Meanwhile moment.js has had 5 releases in the last 4 years. 3 of them in 2022 and 2 in 2023.
7. Worth saying that React really only took off 9-10 years ago, so hooks are damn near the beginning of time for _most_ react devs.
> The docs were revamped 2 years ago and there's lots of dead links to the old docs page which has a scary warning "These docs are old and won’t be updated.
IMO those are not dead links. A link is 'dead' if it links to a page that doesn't exist. Links to old pages with warnings are appropriate in many cases. Many projects are using older versions of react, and devs need to look up info. Not sure this should be seen as a problem.
> Create-react-app was deprecated in February of this year and in their blog post they tell you to use frameworks like Next.js.
Not sure why this is a problem. A very early tool got deprecated, and the react docs recommend the current paradigm. It's not like they're changing their getting started guidance every month, or even every year.
> Remix is now react router v7
If this is a bitch fest about React, then the react docs and CRA are fair game, but remix isn't IMO.
Revamping the docs is not a problem by itself, but take a look at Python or Django, their docs have the same look and feel for older versions of the code. It's totally a minor problem, and if it were the only one I wouldn't be complaining here, but with the plethora of problems it starts to feel like death by a thousand papercuts.
> Create-react-app was deprecated
Going back to Python packaging, while it's much better than C/C++ packaging, people still love to complain about it! That said, pip is not deprecated. For React to just abandon the idea of helping users to create a project and telling them to "go bother someone else about it" does not seems like something a stable ecosystem would do.
> If this is a bitch fest about React, then the react docs and CRA are fair game, but remix isn't IMO.
It absolutely is a bitch fest about React because I inherited a simple site that should have never used React in the first place and it makes it so hard to do simple things without reinventing the wheel, but anyway, I'd say the ecosystem is fair game now that CRA is deprecated and the docs themselves tell you to go to Next or Vite or React Router or Tanstack.
Anyway, the point is that while React might be relatively stable from the point of view of the larger javascript ecosystem, it's still way less stable than it should be and way less stable than browser APIs.
Hooks are ~~7~~ 6 years+8months old, almost 7 years. This may not seem like a significant difference, but IMO it puts them 75% of the way back in time to when react took off versus 50% of the way. That is a significant difference. Please stop repeating the 5 year number. It's _provably_ wrong and not a matter of opinion: https://legacy.reactjs.org/blog/2019/02/06/react-v16.8.0.htm...
> does not seems like something a stable ecosystem would do.
I don't think being a 'stable ecosystem' means owning every part of the dev process, or even most of it; nor have they ever intended to do so (they didn't reimplement npm, webpack, etc). CRA existed to fill a particular need for a time (providing a simpler/more stable interface in front of webpack), and when that was no longer needed by the community, they abandoned it. I don't understand why this matters. Better tools emerged, the React docs/guidance reflect that.
> because I inherited a simple site that should have never used React
I'm sorry that you're in that situation. The React community is not really responsible for that IMO, and I don't think the things you've highlighted have meaningfully contributed to making that worse. I stand by the fact that deprecating CRA once great alternatives emerged was the right answer. Next is probably a good answer for many people too. The react team is and should be writing recommendations for the common case for their library, and the fact that your simple site falls outside of an ideal case for React doesn't mean they're not writing the correct recommendations.
I don't disagree with your overall point here, but if you're going to be super nitpicky and pedantic about this, then you can't call 6 years and 8 months 75% of 10 years.
[1] https://en.wikipedia.org/wiki/Document_Object_Model
[2] https://en.wikipedia.org/wiki/XMLHttpRequest
- Dan Abramov's first big conference talk was in October 2018
- The official release was in React 16.8, February 2019 - that's six years ago
Maybe I'm out of touch, but I don't understand why people think it's so tightly could with the ecosystem
That said, starting with react router or expo is probably the right call depending on the project needs. Routing is not something you want to do yourself, and react native is pretty unfriendly without expo
That is a long damn time in this industry, and class-based components still work just fine.
That's until you have to use it in a real project, for a long time the go to solution was the facebook maintained CRA which is now deprecated. I have spent a lot more time than I'd like to admit migrating webpack versions when it was considered best practises to use class component with decorator but decorator never was standardised in the browser and was never able to get a clean project where running npm install would not give scary warning our project had high vulnerability that was dependencies of dependencies in CRA to the point that it got me wondered that even if the creator of react facebook can't get their shit together, the whole ecosystem is doomed. I have been outside the ecosystem for a year and looking at the shit show that is nextjs it seems things are even worse than before.
I think the only shaky moving part for us right now is styled components being left behind due to the RSC changes, but there's both mostly source compatible rewrites as well as perhaps even better alternatives (vanilla-extract).
Not only did they deprecate it, they refused to acknowledge it's existence in the new docs which is wild to me.
It may have changed in the last year, but if you searched for "CRA", it would get 0 results. Again, mind-blowing considering it was the recommended way to build apps with React.
Instead, it was replaced with a section driving you towards adopting Next. Which then had to be whittled away at and updated to include other options all the while the React team acted hostile to any criticism of it. You either used Next or you were shit out of luck.
> I have been outside the ecosystem for a year and looking at the shit show that is nextjs it seems things are even worse than before.
My thoughts about CRA aside, you don't have to use the frameworks (I still don't). And if you remove Next from the equation, things are actually pretty cool. Suspense is cool, and you'll have to rip React Query from my cold, dead hands.
> running npm install would not give you a dozen high vulnerability package
Yes, this is a serious problem, but mostly an npm messaging problem: https://overreacted.io/npm-audit-broken-by-design/
I'm glad they take good care of backwards compatibility. React was the most common example, I didn't intend to target it specifically.
Not all frameworks are like this though. Svelte 5 introduced many breaking changes, with plans to deprecate the classic Svelte 4 syntax. So that forces many projects to spend time migrating to the newer version.
> The idea that not using a frontend framework will let you focus on delivering features seems reductive...
Agreed, appreciate the healthy arguments. :)
It's a great library, but it does need fewer footguns. date-fns is a good alternative.
A lot of the HN zeitgegist would have you believe React is the opposite, sadly.
But React core APIs remain relatively stable, I've been using React the same way for many years at this point, and you can ignore the parts you don't like, like I'm ignoring hooks and haven't found a single use case where I needed them.
Class based React is great. My old projects are cleanly structured, but harder to change. Class based React also lacks the composability you get with hooks.
Turning it around, what exactly are you unable to do without useState/useEffects?
Usually that array is mapped elsewhere and those child components might also re render if the array is recalculated.
useEffects are when I need to call something outside of react, or when the page gets mounted or I need to call a function when something changes.
I'm still fairly new to this, the above examples may scream bad architecture to those more experienced, all criticisms welcome :)
No criticism really. Your useMemo example is the right use. Your useEffect use is fine, but for things like api calls (which 'call something outside of react' may refer to), you're often better leaning on something like react-query, which is of course built on top of useEffect. So still the right tool, but let others handle many of the sharp edges around that problem.
useMemo dependency smell. This is almost always because your dependencies are wrong. This can often happen if you put a dependency as [object] instead of [object.field] due to how JavaScript maps objects to memory.
> React is a lot more stable than I think you're giving it credit for.
react isn't a framework. didn't read the rest of your post.
you don't have to though!
if you want to do more pure vanilla, understanding signals is really useful — this basically powers svelte's runs and react's hooks and whatever.
I love nanostores, a 286 byte (!) state manager that lets you build highly reactive pages w/o the weight: https://github.com/nanostores/nanostores
flexible tools like tinybase (https://github.com/tinyplex/tinybase) and unstorage (https://github.com/unjs/unstorage) are also super useful
tools like this lets you build highly reactive, engaging sites that load for under 50-100kb
The frameworky code is under 100 lines of code, heavily inspired by what I think is a good idea from React: "components are pure function", the simplest example of the about page beeing visible here: https://github.com/mickael-kerjean/filestash/blob/master/pub...
Since the whole project was migrated over from React, I was able to improve performance to a degree that would have been impossible using React, it's a breadth of fresh air to open the performance and memory tab and to be able to optimise things from there to get 60FPS no matter what whilst preventing massive ram usage because those frameworks runs all sort of code that is out of your control. Also because there is no build system, I was able to introduce plugins to be able to patch frontend code via patch applied dynamically by the server to create features like the recall button to recall data stored on glacier when using S3, dynamic patching of the icons to apply branding and many other things that I would have had to fork entirely before
Anyway, I hope our industry would go more often the vanilla route, es6 is ready for prime time and solve all the pain point we had while internet explorer was still a thing
I hope this becomes common knowledge! There was a period somewhere between when the web platform didn't have good tools to make web applications (plus DOM manipulation was slow) and the mainstream adoption of es2015 in browsers when it made sense to do things like create a virtual DOM to allow for unavailable features at the expense of performance. It did have its place, but we don't need it anymore.
Remove a layer. In 2025 ES6 is ready for prime time, indeed.
Here’s how I approached it for www.lendcalculator.com, a browser-based financial tool with dozens of calculators and dynamic UI — all built without React, Vue, or Angular. How to Build Large Apps Without a Framework Use native modules + ES6 classes Structure your app like a library: each feature (calculator, modal, form) is its own module. Import/export keeps things clean.
Leverage Web Components (if needed) They’re part of the platform now — encapsulation, lifecycle hooks, and reusability without vendor lock-in.
Use vanilla JS + CSS for UI With querySelector, addEventListener, and fetch, you can build surprisingly rich interfaces. CSS Grid and Flexbox handle layout beautifully.
State management via custom stores or events You don’t need Redux — just use CustomEvent, localStorage, or a simple pub/sub pattern. Routing with History API Use pushState and popstate to manage navigation. Works great for SPAs.
Build tools: Vite, ESBuild, or even just Rollup You can still bundle, minify, and hot-reload without a framework.
Projects to Study Obsidian (as you mentioned)
CodeMirror — complex editor, no framework
LendCalculator — my own project, built with vanilla JS and modular architecture
If the answer to that question is “No”, then I’m not conceptualizing a SPA and don’t need a framework.
If the answer is “Yes”, I’d ask where I think that interaction will manifest in the experience and if I can isolate it.
If I am not looking at a SPA use-case, then my focus is on using vanilla HTML and Modern CSS with a tiny bit of javascript (as a scripting language versus as a programming language [1]). Then the remainder of the focus is on data modeling (CRUD), auth flows, and business logic development (what the application will do).
Beyond vanilla HTML, Modern CSS, and a tiny amount of javascript, if I need further client-side interactivity, I would consider something like htmx.
If I need interactivity beyond that, then we’re into SPA territory, but at this point the requirements and application have evolved to that point since we initially said we weren’t building that kind of thing.
Overall, the idea is to progressively iterate on the application, keeping the architecture and dependencies reigned in and aligned with the core objectives.
[1] JavaScript as scripting vs programming language: what I mean by this is JavaScript as a language with many warts is not so bad if its scope is kept small. I believe the issues with JavaScript arise when it is pushed to do too much. This then leads to needing to use Typescript as a matter of pragmatism. I aim to never get to the point of using so much JavaScript that I then need Typescript. I would say using it as a scripting language (to fill in gaps that Modern CSS and HTML cannot handle today) greatly minimizes the need for this progression.
Edit: for a more practical example, you don't need to go down the web components rabbithole just for native reactive components, MutationObserver can watch for your template fragments entering and exiting the DOM and run your init/destruct routines on them. Now you can just push HTML back from the server with fetch or xmlhttprequest or whatever, and as soon as your component's HTML enters the DOM it will be "hydrated" by your init routine via MutationObserver. Honestly, add some event signaling and XHR and you're further along to a smooth SPA-like experience than you might think.
https://developer.mozilla.org/en-US/docs/Web/API
There is a stereotype from the outside world that a great many programmers are autistic. The irrational fear of not using a framework for code in the browser is one of those cases that really screams the stereotype for all to see.
If you are using TypeScript there is inbuilt type checking for the DOM, because TypeScript ships with a very good data type library that describes the DOM in excellent detail.
It’s absolutely not and it absolutely doesn’t. Inheriting a VanillaJS project is often a nightmare because it screams “inexperienced developer” not to use a framework, so the code quality and build processes are often extremely low quality and undocumented.
Look at the comments in this thread. A guy asked a question about vanilla JavaScript, a completely valid question on a site about comments upon programming links, and yet notice all the comments that are instead not about that.
There are so many comments about writing React, which is not what this thread is about. There are so many comments from people talking about themselves instead of actually about programming, much less anything about vanilla JavaScript. There are also comments like yours that are instantly hostility towards anything about original code, a subject that you seem to find horrifying.
Yes, this is incredibly autistic. Some common traits of autism, among many more that each autistic person may or may not have:
* low social intelligence, which in this context displays when developers are only thinking about themselves, what's easiest for them, and cannot think about anything else such as users or business owners
* fragile ego, which in this context results in hostility at subjects that expose a developers shallow capabilities and results in false displays of high confidence to mask deeply felt insecurity
I got tired of working with immature people who, in a technical capacity, could only do a little more that copy/paste template code and were otherwise generally hostile because of high insecurity. I am now in a different line of work where I don't have babysit people.
Writing original code or writing applications without a giant framework isn't challenging, but the subject certainly feels like the world is caving in for a great many people who call themselves seniors or engineers.
I wrote a small internal mini-framework to follow the MVC pattern and Web Components for reusable elements. I also used external libraries: three.js for 3D rendering, sql.js for handling the 3d's models meta-data in a performant way, and @tanstack/virtual for virtualizing large lists and tables.
The biggest benefit was finer control over performance. The main downside was a less comfortable developer experience — it’s harder to find polished, ready-made vanilla-JavaScript components, so you implement more yourself.
2. router https://developer.mozilla.org/en-US/docs/Web/API/URL_Pattern...
The rest should be code organization, not even a build tool.
Can check this example https://github.com/radio4000/components
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guid...
You basically have the same capability you have in python or other ecosystems if you can be bothered to attempt what the OP suggests. Determining an appropriate way to organize your code is actually a really hard problem to solve if you've been leaning on 3rd parties to solve it for you. It's definitely a skill worth developing. Once you have it you will begin to dislike the organizational suggestions of others because they'll never fit your problem as well as a hand tuned strategy.
That being said, as many other comments rightly point out, (for most projects,) if you work directly in the DOM you'll end up recreating what most frameworks do for you. It's really only "worth it" when you can strongly justify it: IE, if you're making a charting library that can handle thousands of datapoints, or a rich degree of interactivity that a usual business web site doesn't have.
But for a typical interactive website: Use whatever framework your peers at your company will be comfortable with.
The fact that I have been able to build a multi-user collaborative editor experience without a single additional dependency is incredible. I previously worked for a well-established and well-funded React team who had this feature on their roadmap for half a decade but still find it too daunting to implement.
Phoenix was a great reminder that a lot of the "frontend engineering" we find ourselves doing as React developers just isn't necessary with the right backend. It's HORRIFIC to look back at all the yakshaving I've done in my career already. Wrangling types (GraphQL, codegen libraries), wrangling queries and data-fetching (react-query, SWR, server components), fiddling with middleware (serverless functions, getStaticProps, CDNs). I've seen teams outright abandon testing because the hours they invested just weren't catching any of the bugs that mattered.
I'm not doing any of that anymore. I'm spending that time refining the core data model, improving test coverage, thinking about go-to-market and making money.
Phoenix may not be a good choice if your product has reached that level of maturity and product-market fit where you really should care about "microinteractions", fine-tuned animations, or advanced use-cases for an SPA like offline support and highly-optimistic UI. But I would argue that even mature products don't truly need these things. Just look at the GitHub UI. I've spent a truly astronomical number of hours in that UI and never wished I had WYSIWYG text editing, or animated skeleton UIs, or the dozen other things that the React community tells us we need.
It comes with some tradeoffs compared to fully client-side state, but it's a really comfortable paradigm to program in, especially if you're not from a frontend background, and really clicks with the wider Elixir/Erlang problem solving approach.
https://hexdocs.pm/phoenix_live_view/js-interop.html#handlin...
Hooks let you do things like have your DOM update live, but then layer on some JS in response.
For example you could define a custom `<chart>` component, which is inserted into the DOM with `data-points=[...]`, and have a hook then 'hydrate' it with e.g. a D3 or VegaLite plot.
Since Phoenix/LiveView is handling the state, your JS needs only be concerned about that last-mile JS integration; no need to pair it with another virtual DOM / state management system.
https://hexdocs.pm/phoenix_live_view/js-interop.html#client-...
Here is an example. Imagine something like a multiplayer Google Forms editor that renders a list of drag-droppable cards. Below is a complete LiveView module that renders the cards, and subscribes to "card was deleted" and "cards were reordered" events.
```
```What would this take in a React SPA? Well of course there are tons of great tools out there, like Cloud Firestore, Supabase Realtime, etc. But my app is just a vanilla postgres + phoenix monolith! And it's so much easier to test. Again, just using the built-in testing libraries.
For rich drag-drop (with drop shadows, auto-scroll, etc.) I inlined DragulaJS[1] which is ~1000 lines of vanilla .js. As a React dev I might have been tempted to `npm install` something like `react-beautiful-dnd`, which is 6-10x larger, (and is, I just learned, now deprecated by the maintainers!!)
The important question is, what have I sacrificed? The primary tradeoff is that the 'read your own writes' experience can feel sluggish if you are used to optimistic UI via React setState(). This is a hard one to stomach as a react dev. But Phoenix comes with GitHub-style viewport loading bars which is enough user enough feedback to be passable.
p.s. guess what Supabase Realtime is using under the hood[2] ;-)
[1] https://bevacqua.github.io/dragula/ [2] https://supabase.com/docs/guides/realtime/architecture
I'm working with JS for already 25 years. Tried all of the frameworks, and continue on doing it. And every time I try something new, the refactoring flow turns most of them into NextJS (if it's very UI rich or customer facing or something very web-oriented), or Vite+React+Tailwind (client) and Hono (backend) if it's more of a tinker toy needing more custom solutions.
The boilerplate with NextJS is cleanest (compared to all the other frameworks) and API is the most straightforward one, and you can safely ignore the vendor lock in. Its just a pretext to hate on NextJS. They all have some kind of a "vendor" lock in. Be it a vendor-or-a-specific-approach-or-whatever-lock-in.
And Vite+React+Hono — simplest to set up for quick experiments, and very powerful with minimal boilerplate. Will probably create a starter for this one, as I have been using this stack quite a lot lately.
EDIT:
You can pretend vanilla JS is all you need, but then your app grows, then you suddenly need types, and state, and more events and their handlers, and SSR or something else. Thus React has been the most stable bet for quite a while for me now.
The vendor lock-in on NextJS is certainly much more egregious than other frameworks. They have their own undocumented build flag to give different build outputs that Vercel uses vs the build outputs that are documented. Hosting nextjs on your infrastructure is not as simple as sticking it into a docker file as with most frameworks
And I would also push back on the idea that every framework has vendor lock in. Remix was so focused on "using the platform" that it basically willed itself out of existence. It's no longer even a framework. Just part of the react-router library. I've also used Astro which is a framework of similar complexity and feature richness as Nextjs and certainly has no "lock in". At least as far as
But for me Lit is too OOP. It feels like Angular. And that all in turn feels like Java. It's just so heavy and constrained (not saying it's a bad thing though). Too much boilerplate for me.
The whole paradigm is different and does not match my preferences. And while subjective, I do believe React with TS, Tailwind, zod, react-query and zustand is the best stack delivering the best balance of boilerplate and code-delivery and easy of use and entry level and dx.
I wouldn't use JQuery for a new project, as you can do almost everything it does with straight DOM manipulation. But there are still some strategic vanilla JS/TS packages that come in handy, e.g. clipboard, mousetrap, file-saver, split.js.
Web Components with Lit is kinda fun, though you'll have to deal with DOM shadow roots or disable them.
I would challenge that using a framework leads to less security. In vanilla JS you've got to use something like dompurify religiously to avoid XSS bugs, and you're tempted by the shiny candy-like innerHTML attribute.
So yeah, JavaScript is nicer nowadays, and browser apis are more consistent than they were then.
> I had a mind-blown-moment when I learnt that Obsidian was built without any frontend JS framework.
The comment you’ve linked to is wrong. Obsidian uses the Electron framework for its “front-end” framework.
In fact, it was even affected by the recent “Tahoe” Electron bug.
https://en.wikipedia.org/wiki/Obsidian_(software)#Availabili...
Electron is just a way of running Chrome without the Chrome UI around it, + a few APIs for accessing native OS APIs. You wouldn't say that Chrome/Firefox/Safari are frameworks just because they execute JS code and has APIs. In the context of this discussion, it is fair to say that Obsidian was built without a framework.
The OP doesn't have a good understanding of what they're asking about, and that's okay. That's why they asked the question.
The linked thread is titled "What framework did the developer use to create Obsidian desktop application?". It's not asking about a web application and specifically referencing a desktop framework with: "He must be using some sort of desktop framework in order to push this out simultaneously right?".
> The latter is about libraries with APIs and mental models for producing the UI & UX of web applications.
Obsidian is not a web application. It is a desktop and mobile application. There is presently no Obsidian web application. So it would be odd to be asking about web frameworks for a non-web desktop and mobile application.
> Electron is just a way of running Chrome without the Chrome UI around it, + a few APIs for accessing native OS APIs.
No, Electron is a complete Javascript-based application framework. It does so much more[1] than that.
1. https://www.electronjs.org/docs/latest/api/app
You're also correct that Electron provides APIs beyond those available in the browser, such as access to the native filesystem. The way I see it, those are mostly some lower-level details it wouldn't be that hard to run Obsidian in the browser, it's just a product choice not to (specifically, it would imply creating a file hosting service). As the Obsidian mobile app demonstrates, Electron is swappable and not needed for Obsidian's core functionality. In contrast, had Obsidian been built on React, it would be rather difficult to simply "run without React" without rewriting the entire application.
How to build a large front-end app on non-web technologies (Swift, C++/QT, C#, etc) is also an interesting question but I didn't understand that as being the topic of this conversation.
[1] get-notes.com
Since we seem to be talking past each other, what do you think the conversation is about?
> "I’d like to know what JavaScript framework (e.g. Vue, React) Obsidian desktop application is using for creating the user interface?
And the answer to that question is: Electron.
Is that not the question?
- Third-party libraries, almost always implemented in JS (technically it could be some language compiled to WASM but I'm not aware of any commonly used WASM framework)
- Dynamically loaded from a CDN or bundled with application code at build time
- Provide an high-level API for creating and updating UI
- Whose implementation edits the DOM (a browser's low-level UI representation)
In contrast, writing an app _without a UI framework_, therefore implies writing first-party JS code that interacts with DOM APIs directly, without that level of abstraction in-between. This is not a common choice these days, and could be considered an impressive accomplishment, hence this Ask HN.
To create that UI, you use the same low-level DOM APIs in Electron as you would in the browser because well, it is a Chromium browser engine.
Example of each combination:
- Framework-using apps running in the browser: Airbnb, Figma
- Frameworkless apps running in the browser: HN
- Framework-using apps running in Electron: Figma's desktop app
- Frameworkless apps running in Electron: Obsidian
I wouldn't consider Electron as an answer to the question. It would be best described as a framework for running web apps as a standalone desktop app, but not a framework for creating user interfaces. Just using Electron doesn't make any progress towards having a solution for managing the complexity of writing code that interacts with the DOM.
You have been conditioned to think frameworks are necessary. That’s not true. You can build anything without them and it would probably be better in many ways. Would you spend a ton of time? Yes. Would your code be harder to maintain? Yes. But it is absolutely doable and not as hard as you’d think
* greater flexibility
* faster execution, My current application produces a large single page application that fully renders visually and executes state restoration in about 105ms localhost or under 200ms across a network
* faster maintenance, I can do massive refactors that touch most files in the application in about 2 hours thanks to thorough use of TypeScript interface definitions
---
Here is how I do it:
1. Just start building your application. Realize that for any significant application the front end UI is probably only about 20% of the application code, or less.
2. Start the UI by writing static HTML. Reserve JavaScript, in the browser, only for handling events and state restoration and execute state restoration only on page load.
3. I prefer to use WebSockets over HTTP as much as possible for absolutely everything I can get away with. WebSockets are full-duplex, where HTTP is not, and have far less overhead. That will substantially reduce the complexity around messaging in and out of the web browser.
4. Everything after that is just execution, planning, refactoring, and feature addition.
5. You can do absolutely all of it in TypeScript now without any compile step. Node.js now executes TypeScript directly, but the browser does not. To solve for the web browser just include the browser code into your Node application using ES6 module import. If the imported code is a function you can convert it directly into a string for transport, and otherwise use JSON.stringify for objects/arrays.
You don't do framework nonsense. That is all there is to it.
For most people writing JavaScript for employment it is absolutely impossible to imagine any universe where a couple of lines of code could be written without something that looks like a framework. That is not a limitation of the language, but rather a limitation of the given developer's imagination. The problem is thus a person problem and not a technology problem.
I do not use innerHTML because that is string parsing, and one of my critical performance steps is to avoid parsing from strings, except JSON.parse when needed.
If the app being built is "large" (which I understand to mean, has high essential complexity), then those tradeoffs matter a lot. If the app is built by a team instead of an individual, the people problems become significant. Those can very well be turned into a technology problem. The technology (framework in this discussion) can be used, among many other things, to establish a consistent way of solving the problems in the application, which alleviates many people problems.
The JavaScript logic in the browser is comparatively small compared to the total application. This is absolutely more true when you remove the bloat imposed by a large framework.
Frameworks do not exist to alleviate problems for the developer. They exist to help the employer with candidate selection and training elimination to expedite hiring and firing. I can understand why a developer who is utterly reliant on the framework to do their job might think otherwise, which is a capability/dependence concern.
A developer choosing to use a framework doesn't mean they are reliant on it, any more than choosing a particular language, library, text editor, tool, etc. It simply means they decided it was a helpful way to accomplish their goal, whether that's to save time, or establish consistency, eliminate categories of problems, avoid re-inventing the wheel, etc.
I don't know if you're aware of this, but you're coming off as incredibly arrogant with your strong claim that frameworks are used by those who don't know better. It's easy on the internet to vaguely gesture at "developers", but most of us are individual who've built software with real users among other demonstrated accomplishments. Strong claims require strong evidence, I hope you have the arguments to back it up.
For example: It’s not that the developer reliant on the framework is less than wonderful. It’s that everyone who differs in opinion is obviously wrong and/or arrogant, because the given developer cannot fathom being less than wonderful.
All you need is the event handler. The event handler can be assigned to a node's event property directly, but if this is for a commercial site with lots of third party analytics/spyware you need to use a event listener instead because those third party libraries will overwrite your event assignments. The event handler receives an event object as an argument with a reference back to the given DOM node.
All the business logic should be stored in a single location representing a given business function, typically far away from the browser logic. It makes sense when you write the code without using a framework. If you write the code and try to make it look like a framework it will look like a framework, unsurprisingly. If you don't do a bunch of framework component insanity it will not look like a framework.
This is a person problem, not a technology problem.
- The complexities of both of these tiny pieces of code is similiar (I would say)
- React needs to load a big bundle
- React spits out a large stacktrace with react internals if something goes wrong
- React is slower
- React code cannot be easily stepped through with the debugger
- React needs a build step
- React performance is very unpredictable (this is more of a problem with many elements and dynamic code)
Your next question might be what you do once your form grows huge. See my other answer to @iliaznk how I handle that.
cleaning up listeners or cleaning up DOM nodes is rarely needed. Just remove the component element and the browser cleans up automatically.
I don't follow. How do you include the browser code without the compiling step?
JQuery was never considered "necessary". It's just a much prettier wrapper around the native dom apis. Arguing against adopting a more readable api (and codebase), is gonna be a tough sell.
Huh? Are you implying the code will never get read by another developer or possibly modified? Are you arguing there is no reason to make your code more readable?
> These things are clear when an outside party can observe your developer habits and inform you where your priorities are as opposed to what you state they are.
What?
If you want to ship an MVP next week and you spend 6 days setting up your env, you've already failed. If you can get a framework with at least 80% of the features you need, suddenly you have 6.5 days left to build the product
You could even write your own browser that uses python instead of javascript. However, it would only be compatible with python html pages. We stick with JS for web development because that is how the "browser framework" is designed. HTML, CSS, JS, etc. are all just part of that framework. If you wrote your own browser, you would need to reinvent all that as well.
I do not miss the early browser days, where different browsers could have been seen a different frameworks. Things go allot better when the browser framework standardized. I still suffer from the IE trauma.
Allot of what we see with JS frameworks ought to baked into the browser framework, but that process is uber slow, because people have to a agree, and people do not like to agree. But it is magic when they do.
For example, people did things in JS over and over and over again and some off those things became browser functionality, and some of them became CSS functionality, and some prompted server and browser changes.
Yes, you are right, the process is slow. But when software has a way for people to customize it, that customization is a very egalitarian kind of feature discovery.
And, when a new developer comes in who knows React, they can ramp up fast and even if they don’t, there are plenty of resources instead of asking Bob how something works - if Bob is even still working there.
You can (and they will) build a massive house of cards on top of anything, in reality that new React dev will the take the same amount of time to ramp up, or worse if you're not careful. Learning the extensive collection of libraries in use, how they are glued together, application architecture, entry points, data and state management, build, deployment, asset management, etc is what takes most of that time. Having documentation for APIs can't help you there.
My advice is to go and learn a proper architecture (MVVM is a great fit in my opinion for MOST things) and then figure out how to implement that with something you enjoy using. My only bit of advice here is that the closer you stick to the underlying web platform the better off you’re going to be so if you want a concrete recommendation something like Lit here gives you all the primitives you need along with great performance, documentation and developer experience.
For platforms, just go native - it's easier than ever with LLM assistance.
With the framework, you have structure. It might not be the best structure to use, but you have one. React lasted really long and is still works today.
Going without framework works if:
- you have a really small project
- you are so advanced developer or have specific use-case that using a framework creates more burden than time saving
- you are creating something so valuable that reimplementing it yourself is worth it
I think that regular website will bleed via maintenance costs in the long-run if you don't use a framework. Instead of paved road with framework migration you would be fixing and debugging bugs in your framework/codebase.
If I had infinite time I'd probably do most of my UI work at this level. I enjoy the process of "how can I do x? I don't have a feature close to x, so let's go build it!".
I think this mostly stems from the trauma of dealing with winforms/wpf/uwp and from having seen JavaScript once before. I feel like I'm restoring sanity in the universe by stacking rectangles in a not-crazy way.
Frameworks don't have to be complex and all-encompassing. When I'm "done" with a project, its UI layer generally only supports exactly what the application needs and not much more.
The reason the big frameworks are so gnarly is that they try to do everything and thus do no one thing particularly well.
You should absolutely build your own UI framework or try laying out a UI from GL primitives. It's like the assembly of UI design. Not for everyone, sure, but I find it peaceful
JSX encourages sloppy HTML. Props are are overly verbose. Hooks are a nightmare. Contexts are gross. Portals are a stupid hack for the very, very stupid reason of making it nearly impossible for parent elements to read anything about their children. The whole "props down, events up" paradigm is woefully naive and half of React exists to get around it because of that.
So yeah, if all you know is react, you've already been on the hard path.
This whole thing about "absolutely everything must be pure, functional reactive code" is just something that people used as a cudgel to get their fellow developers to adopt React. And React isn't even pure, nor does it really do reactivity right! But it makes for cool demos where you can throw together TODO-APP really quickly and never mind that real world applications have much more, cross-cutting complexity that just can't fit into the reactive paradigm.
Yes, managing program state is hard. Full stop. React doesn't actually solve that problem. In fact, I think it makes it worse, because of how top-down, everything-should-be-props it is. Elements should be able to inspect their children. Elements should be able to move them around however they want. And those elements should be written to just take it.
There are major parts of React's modern design that point to the fact that it was clearly taken over by functional programming weenies (not that I hate functional programming, just the kind of people who insist it's the only way).
We only have to look at Hooks to see why. Hooks must only be called at the top-level of a component. But where do Hooks come from? Regular imports that are then accessible anywhere. It violates the open/closed principle. What should be a protected method of a base Component class, so that only implementing child classes can call it, is instead possible to call from anywhere with no indication it's wrong until runtime. That's just bad design, in OOP or FP. Someone, somewhere at Meta, decided "Object-oriented programming is wrong" and went out of their way to avoid it.
A framework establishes a large portion of those patterns upfront. If the framework is a popular one (e.g. React) rather than an in-house one, it makes it easier to quickly ramp up hires and have a lot of documentation/examples online. Popular frameworks also implicitly contain a lot of knowledge from years of feedback from thousands of devs exercising its APIs and providing feedback, so they're typically pretty good at solving common use cases.
Obsidian was initially built by a single developer. One of the best that I have the pleasure of knowing, but when you're one person, it's much easier to create your own pattern and have it work for yourself. They have since hired maybe 2 other devs, with minimal intention of hiring more, so ease of onboarding isn't a significant concern to them the way it would be for most companies (and "large" frontend apps more often than not require larger teams, by definition).
Also, so cool that you got to know Silver! Their team is small but very talented, I look up to them a lot.
- Svelte seems to be fast and simplifying complexity - Laravel is likely the most complete end to end ecosystem I can't rule out. Their hotwire tech is impressive and all built in. - Rails has something similar I believe
It depends on your app too.
React definitely can be heavier than needed especially if the client experience doesn't heavily benefit from it or there's other ways to achieve it.
Things get unwieldy at a certain scale when you don't have something like Typescript regardless of whether you have a framework, but you could start by building up small bits of the frontend stack from scratch. I learned how to do a bunch of manual raw js stuff from scratch originally, then did some professional work with YUI at a huge scale which was probably as close to an event driven frontend system as you can get. Start by figuring out how to listen for url changes, make a network request library from scratch, store data somewhere.
Imo one of the beautiful things we now get along with frameworks is a host of independent build tools that make the feedback loop of testing changes nearly instant and loosely coupled from your code.
In the past we used http://microjs.com/ and similar to find small libs to work with when we threw together marketing websites, not sure how maintained that list is nowadays but the idea is that you can make websites like lego, sometimes you don't need a whole box of Eiffel tower to make a little tree.
If your functionality is well thought out and not super complicated, don't have 10s or 100s of devs on the same project then working without a well known framework could make sense, otherwise there can be steeper learning curve when onboarding new devs, scope creep / bloat, etc that will likely cause issues down the road.
To learn about this you could try to replicate for example the mentioned Obsidian by yourself and research solutions for all the issues you run into, e.g. minimal routing, custom view renders, reactivity, performance, etc.
M -> the MODEL is managed by the backend server, or in-browser via WASM
V -> the VIEW is defined by the HTML and CSS
C -> the CONTROLLER can be solely Javascript in the front-end, solely backend server processes, or most likely a mix of the two
view is css
controller is js
that's why there's 3.
Maybe, if by html we are saying html+dom+webAPIs (indexeddb, localstorage, sessionstorage, cache API), then yeah, you'd be right.
Maybe that's all that "good design" is ... surely there's other systems and architectures where we can confidently reach the same conclusion independently.
For instance, here, I thought we have a well-done separation of concerns that is a bit exotic and people refuse to understand it.
However, as I explored how I disagreed with you, I realized that there's obviously messy concepts that I map differently than you do. So in truth we have an ambiguous design! I'll have to think about this some more.
Maybe usability issues arise not from tools that are "difficult" to use but tools that don't afford a clear mental model on their fit and purpose.
From there one big question is if everything needs to be dynamic or not. You often have menus and navigation that you don't need a framework for and that even something like jQuery is well suited for. On the other hand a framework would probably slow things down for the writing experience in Obsidian, which is the main draw for me to use Obsidian. Once the writing module is written from scratch, it won't seem like such a big lift to also do all the navigation and controls by hand.
That doesn't mean this is the right way for everyone. A lot of times the speed to releasing new features is more important, while the handicraft of the UI/UX is less important and in those cases using a framework is absolutely the right choice.
It uses two 500-line libraries:
This 500-line lib lets you use TSX syntax without React: https://github.com/wisercoder/uibuilder
This 500-line lib implements MVC routing: https://github.com/wisercoder/mvc-router
A large enterprise project will need security, testing, auth, (AI now too). I'd hate to implement SAML without a library, that would be torture, and likely incompatible with most systems.
While I've often written small self projects from scratch, I wouldn't dream of building a large one that way unless you are sure to have an army of engineers and QA.
As an aside, this is where AI code fails as well. Speed of dev is easy, stability over time and compatibility is hard.
I'm currently working on a Company and Employee finder application with Web Components. I found them a lot easier to work with than React.
Also, I'm using a serverless platform I built from scratch. So it's literally all made from scratch with very few dependencies.
https://www.insnare.net/app/#/dashboard/
My priority is code clarity … so while it is not yet tested at scale, https://harcstack.org is aimed to do this on the server side (disclosure - I am the author)
Another determinant is the server side language / approach to templating you choose. With HTMX Python, Rust, Go are all fair game. I’m leading with Raku due to its web framework and ORM.
<https://www.quora.com/What-is-the-tools-and-languages-used-i...>
What about this: https://github.com/jgraph/drawio
That repository only contains the minified code, not the original source.
Like a "proof that open source is working", a show case of a good quality, open source project.
Oh well, time to strike that one off the list.
There is no reason you cannot implement MVVC/MVC or whatever else on JS for reactivity, it’s just that react et al just abstracts that away for you.
You are effectively choosing some method of implementing and abstracting the observer pattern and handling cleanup since you can easily get into a situation where you have dangling references to objects that are never cleaned up (memory leaks) or pretty nasty GC hangs which can make the UI feel bad to use.
Going without a premade framework means it's a fight between you and the pile of complexity. Code will tend to become ugly and slow and hard to change the more you add, so you'll have to go into a cycle that's more like add-refactor-add-refactor. Forward then sideways, forwards then sideways. Do this long enough and you'll will end up with your own libraries and frameworks to manage the complexity.
If that's not enough, Lit enhances it a bit such that either LitElement and/or litHtml can accommodate whichever style you want: https://lit.dev/
As you can see most functionality can be implemented in around 100 lines of code. The advantage is that you can build up functions just for your needs. These functions are super easy to understand because they don't have any dependencies and the whole implementation fits on one or two screens. There is very little indirection.
Another advantage is that most code is actually doing something concrete. With frameworks you often have a lot of code that is just pointing to other code, or is boiler plate code to set something up. Not having any boilerplate code also makes stepping through the debugger a lot easier.
The project has no build step in development which also makes debugging easier. It does have a build step in production.
Most people use frameworks to give the code structure. When writing vanillajs you need to bring the structure of the code yourself. This does take some getting used to but I think it is an essential skill for good programmers to have. On top of that many frameworks push you into structure that does not make much sense in my mind (for example seperating the view and the controller if those are always gonna change together).
So to conclude: My advice for building without frameworks is, just start building. Problems will come up and you can tackle those as you go. Do you think you need to use events in your application? A simple implementation is 10 lines of code and can be written in 10 minutes. If it takes me 1 hour to not use a library I will spend that 1 hour. Most frameworks will not bring that much value. Easy things become short and hard things will become impossible. I think most value is actually brought by the language and the basic api's (DOM, CSS, etc)
Even pre-web. I remember, how quickly I could create Delphi desktop app with drag & drop UI builder more than 2 decades ago. If we speak about generic ERP, you could make dozens of screens in a day.
You just split your app to layers.
That might be for example:
1. Just UI;
2. UI logic (validations, preloading selects...);
3. Transformation of data between data model and UI model
All of the things above lived on users' computers.
All of the things below were on servers.
1. API gateway (terrible things like SOAP, XML-RPC or completely proprietary formats);
2. Business logic;
3. Database
However, you'll inevitably end up with your own after some time. Even if you try your best to stay away from it, you'll need abstractions for:
But you can have minimal layers for that.- Routing
- State
- Rendering
A lot of frameworks try to cover all three of these, and that's where you tend to see a lot of feature churn. I would recommend picking a rendering library (like react) and writing your own routing and state library, even if it's just a light abstraction on top of an existing library. This will make it way easier to upgrade or move in the future, as you will control the API.
- jQuery
- Typescript
- Tailwind
- Novel (https://github.com/owls-on-wires/novel)
- Some other small libraries for things like state management
- Webpack for bundling
An example of it in action: (https://mnty.sh/#serenity)
Could need some js library and that is a maybe.
In general:
If is like a eCommerce site, blog, wikipedia, forum? Not need
If is like a Game, Paint app, form builder? Need.
And how you learn it? Not use a framework, and try to stay with things more minimal, like tailwindcss.
Incidentally, many of the issues people have with React are attributable to this mistake: knowing what a framework is supposed to be, one might expect React to take care of everything, and when it turns out you’re on your own and using React in a complex project means (whether consciously or not) coming up with a homegrown React-based framework of your own, or using a collection of disparate extras that fill in the blanks (like CRA), by then it’s already a hairy mess of a codebase in a desperate need of refactoring.
React lacks most of framework traits. The library does reactive rendering and contains a somewhat restricted set of low-level primitives around it. Even its DOM integration is handled by a separate, optional library (ReactDOM).
Someone could complain it feels like with Vercel the direction for React is to become closer to a framework (now with RSC and JSX runtime being part of React), but still it is nowhere near a state where the phrase “a framework like React” rings true—and, in fact, Vercel’s interests are aligned with keeping React more of a library than a framework so that more people use Next and are upsold Vercel’s hosting, and Next is in fact a framework in the full sense of the word, comparable to Django or Rails.
Similarly, Vue, Angular, Marko, Astro, Qwik, etc. are frameworks. They provide you with project structure, scaffolding, and a lot of batteries. The fact that many of them are, in fact, using React under the hood speaks volumes.
On the first page "Quickstart" all code blocks contain code that contain JSX and call you. They do not even show the part where you need to call render. Copying this code into your codebase will not do anything.
On that same page they also introduce state management with hooks.
On the installation page npm installing react is not mentioned. They suggest using create react app.
Sure you can theoratically use react as a library but I've never seen it and from the website it seems like usage as a library is not the inteded way to use react.
The first sentence "The library for web and native user interfaces" is the only thing pointing to react being able to be used as a library. The rest of it looks like a framework to me.
The second library I remembered (GSAP) writes exactly that on their site.
> A library often does not make you learn new concepts
New concepts are absolutely a thing, take any library that implements any spec and in order to use that library you have to learn subject domain of the spec.
Another example is D3 (the first library I thought of), which does not exactly has “Learn D3” (quite an unorthodox way of assessing whether something is a framework anyway) but which does require you learn a bunch of concepts to use it in an educated way. Just like people do with React, you can obviously not learn these concepts and wing it, and then reap the consequences of resulting awkward code.
> Sure you can theoratically use react as a library but I've never seen it
It is not “used as a library”. It is a library. Being mistaken for a framework is why projects often get burned. People who are aware that it is a library, and use it as such, tend to not to get burned, because then you know you have to either have a simple project, or implement all the framework-y elements that it misses, usually part by strategically picking a set of other libraries to fill in the blanks and part DIY.
Show me a complex project that is not using a framework and I will show you a home-made framework.
I’ve switched to web components with lit/html driven by AI.
Could not be happier.
This is what I used for fontofweb.com's snipper chrome extension.
This is more or less my response to anyone who complains about CSS; just consider the alternatves. CSS is, by an order of magnitude, the most powerful, flexible, accessible, and maintainable paradigm for UI development in existence. In some cases you're talking hundreds of native LOC to implement what a single CSS rule in a class can do.
I don't know why people continue to refuse to do this.
If you're having difficulty thinking outside of frameworks, I would suggest you work on a simple project where you don't use anything like Next.js or React. Start with the bare minimum of tools available to you through the browser API and your backend runtime of choice, then add things on as needed. Try implementing your own routing without using a third-party package dedicated to routing. Work on your own component system, state management, or even a rendering abstraction.
I can guarantee that, once you at least near completion on such a pet project, you'll not only have a better appreciation for what frameworks are doing but realize that they're actually quite overrated in a lot of ways. In reality, it's totally feasible to build an application, especially one for the web, with more of your own code and not having to think about it in terms of how your logic fits within a "framework".
At the end of the day, 99% of frameworks is someone else's opinion. This is I think what makes churn in the JS world painful. The majority of changes are based on a developer's opinion of how things should work, but their opinion isn't necessarily better than anyone else's. Your application is not worse because it doesn't use someone's idea of how an elegant state management system should behave. It's not worse because it's doing its own DOM manipulation rather than handing off all that work to an opaque rendering library. The point is to get the job done. You can make a kickass web application with freaking Backbone or jQuery if you wanted to.
It's not that I don't appreciate frameworks, though I do think it's important for programmers to learn how to move beyond them. Frameworks don't have as big a job as many are lead to believe. Their complexity is primarily arbitrary most of the time. It's not that such complexity can't be beneficial, but bypassing said complexity doesn't require a big brain.
One of the biggest follies I've seen on the web is people calling themselves <framework> developers. No. You're a developer who uses a language and you prefer a specific framework written in that language. And if you don't understand that language, it really doesn't matter what framework you do or don't use—you'll be lost no matter what (or making messes that someone more qualified will have to clean up later).
All of that said: learn your language, learn the syntax, and you'll be able to pick up any framework built for it with a quick read of the docs (unless the docs suck or are non-existent, then it's best to move on to the next option).
Do the things that make you productive. All of this "best practices" crap is a farce. There is no best practice, just popular and unpopular opinions. Do what makes sense to you.
When I write vanilla js I don't have a seperate file called framework.js. There is very little code needed to manage state. Almost all functions in my codebase are doing concrete things.
The main advantage I think React brought is breaking the concept of "separation of concerns" (keeping css, html and js in different files even when they change together). Keeping stuff that belongs together in the same file and mostly pure is what gives the most benefit. You don't need complicated frameworks for that.
When something becomes a framework is a bit blurry. I consider this more of an utility function. It is only 7 lines long. You call it, it does not call you. It gives you back a concrete element, not some abstract intermediate value. It is completely optional. The amount of these utilities you need in a big project is still tiny.I wrote a figma clone (see other comment) with couple of these utility functions. It looks a lot like a regular react project really. Mostly functions (which you might call a component if they return HTML), mostly pure or local state.