Show HN: Aberdeen – An elegant approach to reactive UIs

aberdeenjs.org

213 points by vanviegen a day ago

Yes, another reactive UI framework for JavaScript. Bear with me, please... :-)

I 'invented' the concept for this back in 2011, and it was used (as a proprietary lib) in various startups. Even though many similar open source libs have been released since, and boy have I tried a lot of them, none have been able to capture the elegance and DX of what we had back then. I might be biased though. :-)

So I started creating a cleaned-up, modern, TypeScript, open source implementation for the concept about five years ago. After many iterations, working on the project on and off, I'm finally happy with its API and the developer experience it offers. I'm calling it 1.0!

The concept: It uses many small, anonymous functions for emitting DOM elements, and automatically reruns them when their underlying proxied data changes. This proxied data can be anything from simple values to complex, typed, and deeply nested data structures.

As I'm currently free to spend my time on labors of love like this, I'm planning to expand the ecosystem around this to include synchronizing data with a remote server/database, and to make CRUD apps very rapid and perhaps even pleasurable to implement.

I've celebrated 1.0 by creating a tutorial with editable interactive examples! https://aberdeenjs.org/Tutorial/

I would love to hear your feedback. The first few people to actually give Aberdeen a shot can expect fanatical support from me! :-)

xiphias2 a day ago

Congrats for reaching 1.0! Nice little library, but as it's signals based, it would be nice to make it compatible with the signals proposal (https://github.com/tc39/proposal-signals)

At the same time for me, while it's super nice, in my opinion it just doesn't differentiate enough from other signals based frameworks to get mass adopted / make CRUD apps that much easier to make.

The problem with remote server/database is ,,what data to sync and when'' by the way, it's very different problem from what your framework is solving.

I loved Svelte until I started using SvelteKit and realized how hard the data synchronization part is.

  • vanviegen a day ago

    > Nice little library, but as it's signals based, it would be nice to make it compatible with the signals proposal (https://github.com/tc39/proposal-signals)

    Based on the current proposals, it seems that a signal can only contain a single atomic value, for which changes can be tracked. Aberdeen's `proxy` can efficiently wrap complex data structures (objects within arrays within objects, etc), tracking changes on the level of primitive values (integers, strings).

    For that reason, I wouldn't really call Aberdeen signals based.

    Yeah, "what data to sync and when" describes the problem quite nicely! And indeed, it's entirely different from this library, except that I have a partial solution in mind that may fit Aberdeen rather well... We'll see. :-)

    • MrJohz 13 hours ago

      Aberdeen's proxy is essentially an object of nested signals. In general:

          proxy({
            name: "Foo",
            email: "bar",
          });
      
      is semantically equivalent to

          new Signal({
            name: new Signal("Foo"),
            email: new Signal("bar"),
          });
      
      The syntax for getting a value will look different in each case, but using Javascript proxies you can fairly easily wrap the latter in such a way that it looks like the former. That's what happens with SolidJS's createStore utility, and I assume it's what's happening here as well.

      Aberdeen looks like it's taking Vue's approach of making the proxy/automatically nested case the default, which is typically more convenient as nested reactivity is the thing that makes signals really sing. But it's still just signals, and the same behaviour can always be replicated using manually nested atomic signals.

      EDIT: I've just realised you're OP, so you probably know more about your reactivity system than I do! But at least the way you're describing it, it sounds like it's working the same way as signals do, just with an emphasis on the nested signal case by default.

      • vanviegen 10 hours ago

        That's indeed how Aberdeen works, except that the nested proxy objects are created on-demand, when first accessed.

        'Nested signals' it is then. :-)

  • HWR_14 a day ago

    > The problem with remote server/database is ,,what data to sync and when'' by the way, it's very different problem from what your framework is solving.

    I also feel that the data synchronization (and conflict handling) is where there are a lot of opinions and patterns but few drop in libraries. Although I'm posting this in no small hope that I get corrected with someone pointing out something I should be using.

  • austin-cheney a day ago

    I just read the signals proposal and was not impressed. There is a lot group thought in JavaScript, in Java too but more in JavaScript, around standardizing convenience based upon knowingly bad decisions from convenience abstractions.

    Managing and updating the DOM is stupid simple and that simplicity has nothing to do with state, which a fully separate yet equally simplistic concern. That is something UI frameworks most commonly fail at horribly with a mountain of highly complex state bullshit that is forced on everything. But because framework people cannot architect original applications at any level these failures become indefensible standards enshrined by the most insecure among us.

    • llbbdd 18 hours ago

      examples?

      • austin-cheney 15 hours ago

        Examples of what? Do you mean examples of not using React?

  • boredtofears 13 hours ago

    > Nice little library, but as it's signals based

    How do you reach that conclusion? There is nothing mentioning signals in the docs anywhere.

    • MrJohz 13 hours ago

      The proxy object is a fairly classic way of doing signals (see Vue, SolidJS), and the whole thing about functions rerunning whenever data that was accessed inside that function changes is pretty much the definition of signals.

      It looks like the author is trying to avoid making direct comparisons with signals and existing signal frameworks, which makes sense - depending on your target audience, it can be a bit "inside baseball" - but it's definitely using some sort of signals-like mechanism under the hood.

      • boredtofears 12 hours ago

        Huh. I guess in my trend-chasing days we just called that event driven programming.

        • MrJohz 11 hours ago

          It basically is that, although the key idea with signals is the automatic tracking. There's no sort of `signal.subscribe()` call happening, instead any time you use a signal inside a reactive context, it automatically gets subscribed. This simplifies a lot of patterns that would otherwise be rather complicated. For example, you might have an effect like:

              if (showName()) {
                console.log(name());
              } else {
                console.log("the name is a secret");
              }
          
          In this case, both `showName` and `name` are signals. If `showName()` is true, then the effect will automatically subscribe to both signals, and this function will rerun every time the name changes, or someone toggles `showName`. But if it's false, then the effect will only subscribe to the `showName` signal, which means even if the name changes somewhere else, that won't affect this expression at all.

          In complex situations, it can be harder to see where the reactivity is flowing without the explicit subscriptions, especially at first, but it's also a lot more intuitive to just use data and have it automatically become reactive where it makes sense. It's also the base for most major web frameworks outside of React.

          • boredtofears 2 hours ago

            Thanks, this is an insightful explanation.

Etheryte a day ago

Conceptually, this sounds incredibly similar to what Vue was in the 0.x stage. Are you familiar with Vue perhaps? What would you say are the core conceptual differences? At a glance, both aim for the same idea, that your data is proxied, you detect what changes actually matter, and you try and wrap all of that up in a neat little package. These days of course Vue is a larger beast, but I feel like the origins were very similar to what you have here. Interested to hear what you think about this comparison.

  • lucasacosta_ a day ago

    At first glance and on a syntax level, Vue from the start had part of its code in html syntax and the rest on JS. Aberdeen goes fully into JS.

    So if I get it right, in Aberdeen there would not be any pure html written at all, right? Is that the "ideal"? Or it would be more of a hybrid with Aberdeen accompanying plain html?

    • vanviegen a day ago

      Correct!

      As far as I know, Vue has always had its own HTML-based template engine, with special HTML attributes for conditions, loops, etc. Different trade-off.

      Since Vue 3, it does indeed rely on `Proxy` for its reactivity, like Aberdeen.

      The idea is the write whole applications without HTML. We've done some pretty big projects in this style, and in terms of DX and velocity it's actually really good. Recycling (tiny) components becomes really easy, as it's all just JavaScript functions.

      • dsego 21 hours ago

        Aberdeen looks a bit like the venerable Mithril in terms of DX.

        • pdfernhout 19 hours ago

          Yes, I noticed that similarity too a bit -- from the use of a "HyperScript"-like approach (although Mithril can be used with JSX too). https://mithril.js.org/hyperscript.html

          Aberdeen is innovative in that it's great to be able to avoid the VDOM in a technical sense. For fun, years ago, I messed around with a different technical approach of just rendering HTML from code directly (similar to Aberdeen to that extent), but instead of an observables-ish idea, I used Mithril's approach of hooking into the event listening system to trigger redraws by wrapping event handlers. Of course, I quickly ran into the issue of losing state like cursor insertion points in editors when re-rendering (which is what motivates the vdom approach). I can wonder if maybe the HTML standard itself could be upgraded somehow to support that approach of replacement of HTML widgets but with retained widget state if an ID or such remains the same?

          While I applaud Aberdeen as an experiment, with Aberdeen, it seems problematical developer ergonomics to have everything be a reactive pipeline of some form of observables or signals. It's not especially more terrible than many other systems (e.g. Angular in practice, encouraging the use of RxJS Observables as a new way of encouraging writing hard-to-understand code). As Aberdeen has the previously mentioned innovation of avoiding the vdom, overall it might be a win for people who like that style of coding.

          But by comparison, I have found Mithril is so much easier to work with -- given the core idea of just assuming the UI is "dirty" after any listened-for event happens and re-rendering it.

          It's true though that Angular has a "Zones" approach to redraw when state changes which hides a lot of complexity but has OK developer ergonomics similar to Mithril in that sense -- until you get mired in RxJS code encouraged by the platform (at least when I last used it seven or so years ago). And then Angular code get mired in various other Angular-isms which requires bloated code in practice with lots of files to do the simplest component. Yes, you can try to work around a proliferation of files, but that is not the Angular way, or at least was not several years ago. And Angular advocates would argue the standardization is a big win for larger projects with multiple developers. YMMV.

          Also, dsego, thanks for submitting to HN in 2022 the essay I wrote on all that (which I just saw thinking prompted by seeing this article to check if it had already been submitted somehow): https://news.ycombinator.com/item?id=25194873

          For others, this the essay which goes into more depth comparing developer ergonomics of React, Angular, and Mithril from my experience: "Why I prefer Mithril over Angular and React" https://github.com/pdfernhout/choose-mithril

          For a simple Mithril example with no special Observable or signal magic, see, say: https://kevinfiol.com/blog/simple-state-management-in-mithri...

              let count = 0;
          
              const Counter = {
                view: () =>
                  m('div',
                    m('h1', 'Counter'),
                    m('p', count),
                    m('button', { onclick: () => count += 1 }, '+'),
                    m('button', { onclick: () => count -= 1 }, '-')
                  )
              };
          
              m.mount(document.body, Counter);
          
          The state is stored in just a regular JavaScript variable ("count" in this case), not a wrapped something or other special object (like Aberdeen or any other Observable or signal approach requires). This is made possible in Mithril in this case by the onclick method being transparently wrapped bu the HyperScript "m" function (or JSX equivalent) to call m.redraw() after the click event is handled. As with Mithril's HyperScript approach of leveraging JavaScript to generate HTML instead of inventing a weird non-standard templating system like JSX, Mithril's wrapping of event handlers leverages the ability to just simply define state in JavaScript closures or objects -- or whatever other more complex approach you want including even RxJS observables (as long as m.redraw() is called as needed if you make data changes outside an event handler). Lego Horie, Mithril's original inventor, just was brilliant with Mithril at creating great developer ergonomics.
          • vanviegen 19 hours ago

            > it seems problematical developer ergonomics to have everything be a reactive pipeline of some form of observables.

            Why do you think that would hurt ergonomics? You can use whatever data structures you normally would (including typed class instances), you'll only need to proxy() them to be observable.

            • pdfernhout 19 hours ago

              Thanks for the replies. Having to add "proxy" or similar everywhere is the ergonomics issue to me (others might disagree).

          • vanviegen 19 hours ago

            Here's what your Mithril examples looks like with Aberdeen:

              let count = proxy(0);
              
              function Counter() {
                  $('div', () => {
                      $('h1:Counter'),
                      $('p:'+count.value),
                      $('button:+', { click: () => count.value += 1 }),
                      $('button:-', { click: () => count.value -= 1 })
                  })
              }
              
              $(Counter);
              // Or just Counter() would work in this case
            
            The '.value' is needed because we can't proxy a number directly. This extra step goes a way if you have more data to store, like `person = {name: 'Peter', age: 42}`.

            Besides that, looks pretty similar yeah! Works rather differently though. :-)

            • pdfernhout 19 hours ago

              Thanks for the example. I agree they look very similar, which is what dsego saw intuitively and commented on.

              The difference in the two examples is essentially whether things need to be proxied or not.

              If someone does not mind that proxying, then Aberdeen has an advantage over Mithril by avoiding the complexity of the vdom internally. So, presumably the rendering code is simpler, which may have some benefits in performance and also debuggability in some cases. You can get surprising vdom-code-related errors from Mithril sometimes for something misconfigured in a hyperscript "m" call. Also the vdom regeneration can sometimes have issues if you don't specify a "key" for each m() widget, needed sometimes so the vdom can be sure to replace DOM nodes efficiently and correctly.

              If someone does mind the proxying requirement (like me), then Mithril has the advantage in that regard of seeming simpler to code for (without the data wrappers). Mithril applications also may be easier to debug in some other cases -- because you don't have an extra proxy levels as indirection in viewing your data model.

              So, both approaches have their pros and cons depending on preferences and priorities.

              • vanviegen 10 hours ago

                The performance advantage as compared to Mithril is not so much in ditching the complexity of the VDOM, but in not having to rerender (and then diff) the entire UI on every (possible) change.

                Whether that's significant depends on what you're building of course.

tacitusarc a day ago

Congrats! Building something like this is not trivial. You should be proud.

I think it would be useful to have an example of how to make “widgets”, larger components with more complex behaviors that could be reused across many applications.

I recommend making the HTML boxes scrollable, and allowing the code to scroll x. Reading wrapped code on a small screen is difficult.

  • vanviegen a day ago

    > I think it would be useful to have an example of how to make “widgets”, larger components with more complex behaviors that could be reused across many applications

    Yeah, that's probably a good idea. Aberdeen doesn't prescribe any 'standard' for how components should be implemented; they're just functions that draw stuff after all. But figuring out some best-practices may not hurt!

    > I recommend making the HTML boxes scrollable, and allowing the code to scroll x. Reading wrapped code on a small screen is difficult.

    You'd think that in 2025, I'd remember to test my pages on a mobile phone, right? Doh! :-)

catlifeonmars a day ago

Why not JSX? There’s no real cost to making the API JSX compatible, from what I can tell, and tsc has builtin support for transpiling JSX. It would also make porting code a lot easier. I’m only saying this because the type signature of $ is so similar to createElement.

As an aside, I really like the class name and text content ergonomics (e.g div.someclass, span:some content). Reminiscent of pug/jade

  • vanviegen a day ago

    I don't particularly like how control logic needs to be embedded within JSX using ?ternary : operators and .map(() => stuff) within the HTML.

    Also, in order to transform JSX into individual rerunnable functions, we'd need a whole different transpiler. I like being able to code browser-runnable JavaScript directly.

    To each their own. :-)

    • recursive 20 hours ago

      > I don't particularly like how control logic needs to be embedded within JSX using ?ternary : operators and .map(() => stuff) within the HTML.

      It doesn't.

      In my[1] framework, there is JSX, but control flow like map is done with a function.

          <ul>
            { ForEach(model, item => <li>Fruit: { item }</li>) }
          </ul>
      
      There is a lambda there, yes, but at the top level it's a ForEach() function call.

      Likewise, it is possible to use get conditional elements in JSX without using react's ugly approach.

      [1] https://mutraction.dev/

    • gavinray a day ago

      You can just write an IIFE expression, and use regular imperative logic like if/else and switch statements.

      If you find the syntax ugly, you can create a function like "run(expr)" to wrap it, similar to Kotlin's method of the same thing.

          <div>
          {(() => {
              switch (status) {
                  case Status.LOADING: return <div className="loading">Loading...</div>
                  case Status.ERROR: return <div className="error">Error: {error.message}</div>
                  case Status.SUCCESS: return <MyComponent data={data} />
              }
          })()}
          </div>
    • WorldMaker a day ago

      > Also, in order to transform JSX into individual rerunnable functions, we'd need a whole different transpiler.

      I don't think you would. `<Component prop="example" />` gets converted by current transpilers into `jsx(Component, { prop: "example" })`. The `Component` itself is passed as is. In the case of Components that are just functions, that passes the function as-is, as a function you can just call as needed.

      JSX was built for "rerunnable functions". It's a lot of how React works under the hood.

      • vanviegen a day ago

        The problem is that JSX transpilers will put child nodes in an array, instead of in an anonymous function, meaning there is no easy way (without transpiler magic) to rerender just a part of the component.

        This JSX:

          <section><h1>Welcome</h1>{data.enabled ? <input /> : "disabled"}</section>
        
        Which becomes this with Babel:

          _jsx("section", {children: [
            _jsx("h1", {children: "Welcome"}),
            data.enabled ? _jsx("input", {}) : "disabled"
          ]})
        
        But we'd need something like this to fit Aberdeen:

          _jsx("section", ()=>{
            _jsx("h1", ()=>{_jsx("Welcome");});
            if (data.enabled) _jsx("input", {}) else _jsx("disabled");
          }})
        
        In React you can, with some effort, limit virtual DOM rerenders to a single component. Aberdeen rerenders fractions of components by default, if that's all that needs to happen. That's why it works well operating directly on the actual DOM, without a virtual DOM inbetween.
        • WorldMaker a day ago

          Your `_jsx` function can auto-wrap children in a function. Also, you can just pass functions as children, too if you really want to make the JSX author work for it:

              <section><h1>Welcome</h1>{ () => data.enabled ? <input /> : "disabled" }</section>
          
          That doesn't even look half bad to me. It looks really close to many of your other examples.

          > In React you can, with some effort, limit virtual DOM rerenders to a single component. Aberdeen rerenders fractions of components by default, if that's all that needs to happen. That's why it works well operating directly on the actual DOM, without a virtual DOM inbetween.

          A lot of that depends on what your model of a "component" is. React will rerender fractions of a component in advanced areas such as a Error Boundaries and Suspense.

          Also, for what little it is worth, my JSX-based library isn't a virtual DOM, operates directly on the actual DOM, and generally renders components once and only once in their lifetime, because it binds all changes even more directly to specific DOM properties.

          • MrJohz 12 hours ago

            > Your `_jsx` function can auto-wrap children in a function.

            It unfortunately can't, because by the time the jsx function gets a chance to run, the expression has already been evaluated, whereas the whole point of a framework like this is to be able to run that evaluation repeatedly.

            So what you'd need to write to get that to work would be something like

                <div>{() => user.name}</div>
            
            for every single case where you're interpolating a value, which starts looking kind of ugly. It also has its own pitfalls. For example, your example with a ternary in it would unnecessarily rerender (i.e. completely unmount and recreate) the child elements if the input changes but the conditional still evaluates to the same thing. (E.g. if data.enabled was 1 and we set it to 2, what we want is to have no change in the DOM, but what we'd get the input element being unmounted and remounted.)

            There's also just the general issue that JSX requires some sort of transform. This isn't necessarily bad - I use SolidJS a lot which explicitly makes use of the chance to optimise the JSX rendering at compile time - but there are situations where you want to avoid that, and having a syntax that's focused on the "no compile step" case is useful.

        • ibash a day ago

          Modern react developers forget that if statements exist. When react was class based it was trivial to do:

            render() {
              if (this.dontNeedToRender) {
                return null
              }
            }
          
          Now, because hooks can't be skipped, react developers jump through many hoops to use an if statement during rendering.
          • prothy a day ago

            I might be missing your point, can you elaborate? If you want to write an if statement you just do it at the end of a component, after the hooks. It's a common pattern.

    • CooCooCaCha a day ago

      You don't have to do that in the html if you don't want to. You can easily assign those operations to a variable then insert the variable into the html.

    • i_dont_know_any a day ago

      re: syntax, I agree, it's stopped me from ever trying React/JSX-based frameworks, which I am sure is an over-reaction.

      I have a POC syntax extension (babel parser fork) I named JSXG where I introduced "generator elements" which treats the body of the element as a JS generator function that yields JSX elements.

      The simple/naive implementation of just running the generator was okay, but I (perhaps prematurely) worried that it would be not ideal to have the resulting list of child elements be actually dynamic-- as opposed to being fixed size but have false/null in place of "empty" slots and also using arrays for lists made by loops.

      So, I also had a transform that followed conditional branching and loops etc. and made a template of "slots" and that resulted in a stable count of children, and that improved things a whole lot.

      It's been a while since I revisited that, I should try and find it!

      Comparisons below.

      Aberdeen:

          $('div', () => {
            if (user.loggedIn) {
              $('button.outline:Logout', {
                click: () => user.loggedIn = false
              });
            } else {
              $('button:Login', {
                click: () => user.loggedIn = true
              });
            }
          });
      
          $('div.row.wide', {$marginTop: '1em'}, () => {
              $('div.box:By key', () => {
                  onEach(pairs, (value, key) => {
                      $(`li:${key}: ${value}`)
                  });
              })
              $('div.box:By desc value', () => {
                  onEach(pairs, (value, key) => {
                      $(`li:${key}: ${value}`)
                  }, value => invertString(value));
              })
          })
      
      JSX:

          <div>
            {user.loggedIn ? (
              <button
                className="outline"
                onClick={() => user.loggedIn = false}
              >
                Logout
              </button>
            ) : (
              <button onClick={() => user.loggedIn = true}>
                Login
              </button>
            )}
          </div>
      
          <div
            className="row wide"
            style={{ marginTop: '1em' }}
          >
            <div className="box">
              By key
              <ul>
                {Object.entries(pairs).map(([key, value]) => (
                  <li key={key}>
                    {key}: {value}
                  </li>
                ))}
              </ul>
            </div>
      
            <div className="box">
              By desc value
              <ul>
                {Object.entries(pairs).map(([key, value]) => (
                  <li key={key}>
                    {key}: {invertString(value)}
                  </li>
                ))}
              </ul>
            </div>
          </div>
      
      JSXG:

          <*div>
            if (user.loggedIn) {
              yield <button
                      className="outline"
                      onClick={() => user.loggedIn = false}
                    >
                      Logout
                    </button>
            } else {
              yield <button onClick={() => user.loggedIn = true}>
                      Login
                    </button>
            }
          </*div>
      
          <div
            className="row wide"
            style={{ marginTop: '1em' }}
          >
            <div className="box">
              By key
              <*ul>
                for (const [key, value] of Object.entries(pairs)) {
                  yield <li key={key}>
                          {key}: {value}
                        </li>
                }
              </*ul>
            </div>
      
            <div className="box">
              By desc value
              <*ul>
                for (const [key, value] of Object.entries(pairs)) {
                  yield <li key={key}>
                          {key}: {invertString(value)}
                        </li>
                }
              </*ul>
            </div>
          </div>
      
      Edit:

      Come to think of it, I think it may have been <div*>...</div*> or even (:O gasp) <div*>...</div>.

      • vanviegen a day ago

        That looks cool!

        I think it would be pretty easy to transpile this (or something like it) to code Aberdeen can consume. In fact, it would probably be a lot easier than transpiling for React, as Aberdeen uses immediate mode the `for` problem in your comment below wouldn't be a problem at all, so no return values to worry about.

        I'd use something like this myself for larger projects, I think. But asking other developers to use a different programming language just to use your library usually doesn't fly well. :-) Coming to think of it, I find it actually kind of surprising that JSX succeeded.

      • i_dont_know_any a day ago

        Ah, the roadblocks are coming back to me.

        The first was using </* as the marker for the end tag of a JSXG element. It worked, but it seemed like it wouldn't be too well received as parsers today treat /* as a comment in that spot iirc.

        Edit: The other premature concern/feature was the ability to have a for loop NOT render to an array and rather be inline.

        Normaly

            for (...) { yield <>...</> }
            // becomes
            [[<>...</>, <>...</>, ...]]
        
        But I added a mechanism using string directives that would inline it:

            "use jsxg:inline"
            for (...) { yield <>...</> }
            // becomes
            [<>...</>, <>...</>, ...]
  • jitl a day ago

    JSX is a better data structure than strings for expressing views, but this doesn’t use strings or any other data structure.

    The beauty of the OP’s approach is the immediate mode render approach. JSX is a data structure, immediate mode implies a structure from the order of execution of computation.

    You need JSX to pass around fragments of a view, unless your view is made entirely of computation, in which case you can just pass around functions.

    • WorldMaker a day ago

      JSX does not have any inherent data structure, it immediately converts to function calls. React is well known for taking the JSX calls and converting them to a reusable data structure, but that's React's thing and React's data structure is somewhat unique to React, other virtual DOM's have different data structures.

      You can do "immediate mode" JSX. There's nothing technical stopping you, and there are at least a few libraries out there that do.

      • zelphirkalt a day ago

        No matter how one turns it, JSX is still something different from plain JS. It is a kind of format, that needs to be parsed and interpreted. Writing only plain JS from that perspective is a purer approach than having JSX anywhere.

        • WorldMaker a day ago

          There's no embedded interpretation in JSX, it's a direct "sugar" for a function call pattern. If you are already using Typescript, it's a "free" syntax sugar that Typescript supports well.

          It's less pure in that it doesn't run directly in the browser today, but it is also way more pure than the template compilers in Angular and Vue and others.

        • robertlagrant a day ago

          > It is a kind of format, that needs to be parsed and interpreted

          Yes, but this happens at build time, not run time.

  • 90s_dev a day ago

    The signature looked compatible with JSX to me. You could probably easily create JSX functions that just use $ internally, and make your web build tool's react-auto-import setting point to it.

phartenfeller a day ago

Not for me as I like looking at actual HTML but definetly intersting. Good job!

I also like Svelte which uses it's own language and needs transpilation. I think that's key to elegance as JS was not really designed to control layout, style and logic all at once.

  • vanviegen a day ago

    It takes some getting used to, but the advantage of using JavaScript for layout, is that it mixes well with your control logic. JSX makes loops and ifs hard to look at. Svelte/Vue/etc invent templating languages with their own control logic - while we're already running within a full-fledged language.

    Originally, we did this style of programming in CoffeeScript. Without all of the braces, it looks at lot cleaner. :-)

    Svelte is one of the frameworks I stuck with the longest. There's a lot to like. What I didn't like was the gotchas around change tracking. I guess runes is intended to fix that, but... damn. :-)

GordonS a day ago

Which Aberdeen was the inspiration behind the name? (I'm near the original Aberdeen in Scotland, but I know there's at least one in Australia and several in the US too).

  • vanviegen a day ago

    The Scottish one! My girlfriend used to live there for half a year doing her Master's.

    I also once made Glasgow (a React-clone created for educational purposes, when I was a CS teacher). So named, because it is ugly. ;-) https://www.npmjs.com/package/glasgow

    I'm not sure what Edinburgh is going to be yet, but it would probably need to be rather iconic. :-)

  • test1235 a day ago

    There's also one in Hong Kong, surprisingly.

    Fellow Aberdonian represent

    • amiga386 a day ago

      Named after Lord Aberdeen (born in Edinburgh)

      https://en.wikipedia.org/wiki/George_Hamilton-Gordon,_4th_Ea...

      > His diplomatic successes include organizing the coalition against Napoleon in 1812–1814, normalizing relations with post-Napoleonic France, settling the old border dispute between Canada and the United States, and ending the First Opium War with China in 1842, whereby Hong Kong was obtained.

jmull a day ago

> Express UIs naturally in JavaScript/TypeScript

I would disagree there. Conceptually, you're writing reactive HTML and CSS. I think it would be a lot more natural to express HTML and CSS using HTML and CSS (with extensions to add reactivity), not Javascript/Typescript.

(Svelte is an example of this, though it has it other issues, IMO)

  • vanviegen a day ago

    Meh, I think there's nothing magical about the HTML syntax that makes it particularly great for expression DOM structures. Except familiarity of course.

    • jmull a day ago

      It's not a question of HTML's syntax.

      1. HTML is a widely implemented standard. What you learn and know about HTML, and what you create with HTML is widely applicable. Not so much for your HTML alternatives, like Aberdeen.

      2. HTML is what the browser accepts, which means you end up dealing with it anyway, just with a transformation in between, making things harder. The bigger the transformation the harder it is. To develop with Aberdeen you still need to know HTML, but you also need to know Aberdeen and how it transforms into HTML. And, you typically end up needing to learn how to mentally transform backwards as you debug, e.g., using the browser dev tools, looking at problem HTML, and deciding how to change the Aberdeen code to fix it.

      • vanviegen a day ago

        Those are good points. Two things though:

        1. I'd guess that of all the knowledge a web developer accumulates about HTML and CSS over the years, less than 1% concerns HTML's syntax. Everything else still applies.

        2. As a client-side JavaScript developer, what you're actually dealing with is usually not HTML, but the DOM. JSX only resembles HTML rather superficially. It gets translated to a series of DOM method calls. HTML itself has no support for event handling, control structures, composition, etc.

        That being said, going with pure JavaScript is of course a trade-off that also has downsides, such as indeed familiarity, and not being able to just paste a block of static HTML into your code. (Though there's a tool to help with that: https://aberdeenjs.org/Tutorial/#html-to-aberdeen)

charles_f a day ago

You might consider to add shorthands for at least the common components (eg div(...) instead of $('div',...). The funny thing is that I somehow ended up rebuilding a simple version of that a couple of times, and it looks a bit more natural (plus it gets old typing that "div", ).

  • vanviegen a day ago

    Yeah, I know what you mean - I went back and forth about that API decision a couple of times. :-)

    What landed me on $ was that a don't like polluting my namespaces with function named `a`, `b`, `p`, `form`, `input`. And you'll often want to add CSS classes, so you'll have to create a "string" anyway. Also, we'd need a generic $-like function anyway, for less common tags and for attaching properties/behavior/text content to the 'current' element.

    Back in the CoffeeScript days, we did at some point expose HTML tags as function though, and it made for awesome looking (though very error-prone) code! :-)

      form ->
        h2 "Sign up"
        input placeholder: "Name", bind: name
90s_dev a day ago

The two downsides you list ("lack of community" and "lack of ecosystem") are not really downsides. If something is good enough, people will simply adopt it out of excitement, and those two problems will disappear entirely. See Rust and React.

This looks and sounds incredibly clever, and seems to be what I always wanted React to be. I'm eager to try it out!

Could you make a demo of a simple Todo list app in it that lets you edit items, toggle them complete individually, inverse all items, and delete all done items? That way we can see how this works with relatively complex hierarchies.

  • throw10920 a day ago

    > "lack of community" and "lack of ecosystem"

    Yes, those absolutely are downsides - they materially affect my choice, as a developer, to use the framework or not.

    The fact that they're solvable doesn't make them not negative. Other frameworks could solve their problems, too - that doesn't mean that they aren't problems now.

  • soulofmischief 17 hours ago

    I think what you're looking for is mithril. It checks every one of these boxes, except it has a nicer developer experience and still has lifecycle methods, which seem much easier to reason about and test than Aberdeen. Seriously, check it out. You'll be impressed. You can learn the API in a day, and it's super stable and thoughtful. I've been using mithril for a decade and the only time I don't reach for it is if web components solve my problem (smaller projects, non-SPAs)

    https://mithril.js.org

    • 90s_dev 5 hours ago

      I used Mithril in a few projects about 13-14 years ago. It was okay.

  • vanviegen a day ago

    Thanks!

    Yeah, creating a todo-list demo was actually on my, eh... todo-list! :-)

    I'll probably implement this one soon: https://todomvc.com/

    • 90s_dev 2 hours ago

      I just saw your todo list. One issue is that all the work is done in the view. Suppose I want to toggle or clear items programmatically instead of via UI. This is why I suggested the todo example, because it goes one level deep, which is more than zero, just enough to show-case how the model and view interoperate in a way that's not just the same as backbone.js, which so far this looks exactly like.

      [edit]: That's also why I suggested a button to inverse all todo items. This would have forced you to pull some code out of the view constructor for a todo item and I wanted to see where it would go and how it would all connect.

satvikpendem a day ago

This seems to be essentially what Flutter with Dart does, functions that emit UI rather than HTML-like code.

omneity a day ago

Very interesting approach, congrats on your release!

Have you noticed any practical performance overhead related to the usage of Proxy objects? Granted, that might not be the area of focus you have in mind for Aberdeen. I'm just asking as I mused on a similar idea some time ago and always felt hindered by this point.

My second question is related to composition and reactivity (computed fields and other transformations happening _outside_ the components). Do you see any particular pattern working well with Aberdeen?

  • vanviegen a day ago

    I had similar concerns when I made the jump from a custom data class (with methods like `set`, `get`, `increment`, `push`, etc) to a transparent `Proxy` around native JavaScript objects. I did some quick benchmarks back then, and concluded that it was actually not meaningfully slower. Modern JavaScript engines are awesome! :-)

    Aberdeen is not in js-framework-benchmark yet, but I've done a pull request https://github.com/krausest/js-framework-benchmark/pull/1877 -- By my own testing, performance is similar to something like react (while of course destroying React on time-to-first-paint, bytes transferred, etc). However, this test is not a particular good fit for Aberdeen, as lists in Aberdeen are always sorted by a given key (it maintains an ordered skiplist for the data). The test only requires the easy case: sort by creation time. So Aberdeen is doing some extra work here.

    With regard to reactive data transforms: Aberdeen provides some (reactively 'streaming') helper functions for that (`map`, `multiMap`, `partition`, `count`). But these are mostly based on the `onEach` primitive, for reactive iteration. Take a look at the `map` implementation, for instance: https://github.com/vanviegen/aberdeen/blob/a390ce952686da875...

    Is past projects, we've been using things like this a lot. Pretty easy and fast!

zendist a day ago

There are many overlaps with SolidJS. How is this project different, ignoring the obvious; that you don't support JSX.

pc86 a day ago

I haven't had time to dig into this yet but I played the tic-tac-toe demo at the bottom of the page and it seems pretty interesting. The code is easy to understand.

How do you see this being used in a "real" application? Do you see it providing most/all of the reactivity or integrating with an existing framework or library?

  • vanviegen a day ago

    That's good the hear!

    I intend this to be used to write full applications. I've used to proprietary spiritual predecessor to Aberdeen for pretty large projects.

zareith a day ago

This looks cool, love the API.

I don't any support for lifecycle hooks (eg. something like onMount when the returned node will be attached to the document) in the component api. In absense of those, I imagine integrating with vanillajs libraries will be difficult (eg. codemirror, slickgrid etc.) Curious what your thoughts in the matter are.

  • vanviegen 21 hours ago

    While in a rendering scope, `getParentElement()` will get you access to the raw DOM element. As long as you don't detach/move it, you'll be fine letting third-party code loose on it.

    Aberdeen has one life cycle callback: `clean`, which is called right before rerunning or destroying a scope.

    I think that's enough for just about anything you'd want to do.. ?

bufferoverflow a day ago

1) this is the opposite of elegant. Something that can be expressed in a few clean lines of html + css turned into a wall of barely readable JS, where you have to jump between functions just to find the correct place in the DOM tree.

2) why do you call it "declarative"? This is procedural. React or Vue is more declarative (you declare the state, and it renders based on the state).

  • vanviegen a day ago

    1. You see a "wall of barely readable JS" somewhere? Eye of the beholder, I suppose.. ?

    2. Perhaps "reactive" would have been a better term. In fact, I'll change it. To me, the difference with procedural is that functions automatically rerun when the data they access changes.

jdonaldson a day ago

I remember the concept of the virtual dom had to be explained very carefully so people understood why it led to more efficiency when rendering (e.g.) scrolling content.

Is there a technical trick that one takes that can beat a virtual dom use case? What is it?

  • vanviegen a day ago

    Yes, it's an easy trick: just rerender the tiny parts of the DOM that actually need to change. Like Svelte does, only with run-time magic, instead of compile-time magic.

  • MrJohz 11 hours ago

    The virtual DOM doesn't really lead to better efficiency, that's a bit of a myth. It's really a UX tool: it means that you can treat rendering as a pure operation (i.e. (state) => UI), and recreate as much of the UI tree as you like whenever you want, without interacting with the DOM directly. Then, at a different point in time, you can diff that virtual tree with the real DOM tree and only make the necessary changes.

    This is always going to be slower than a framework like this or Solid or Vue or whatever, which just skips to the last step and only make the necessary changes in the DOM.

    Another way to think about it if you're familiar with different forms if GUI rendering is that React is an immediate mode abstraction sitting in top of a retained mode system. In React, theoretically, the render functions could run at 60fps, constantly churning out the same values. In practice this isn't very performant, so React has its own rubrics for when to rerun the render functions, but that's why, for example, it's fine for React to do its weird double-render thing in dev mode. However, at the end of the day, the DOM doesn't work like that, so React needs to diff everything back into DOMland.

lerp-io 14 hours ago

this is pretty cool actually. i remember 10 years ago when I used react but in coffeescript and without the ugly jsx and just renamed the "createComponent" function to "h". this kinda feels like that but upgraded with the signals state

tgv a day ago

I don't dislike it, but how does this differ from e.g. Vue? And does it know which functions changed, or does it run the top-level function on each update?

For those who prefer HTML: I'm sure this can be adapted with JSX or a template pre-processor.

  • vanviegen 21 hours ago

    > I don't dislike it

    That must count for something! :-)

    Re "Vue": https://news.ycombinator.com/item?id=43937303

    When running a function through `$()`, it will keep track of proxied data that was read, and of all DOM changes made. When data changes, DOM changes are reverted, and the function is rerun. As at least such a function exists for each DOM element that has children, it's pretty finegrained. Also, Aberdeen is smart about reactive iteration.

    With enough transpiler magic, impossible is nothing! :-)

sesm 19 hours ago

Can a component have some local state that is passed down to children? Or any data has to be in a proxy object that is outside of any components?

  • vanviegen 19 hours ago

    Sure! As long as the scope that creates the state does not subscribe to it, state can be created anywhere.

    In the following example, each of the three 'things' has its own votes state:

      function drawThing(title) {
        const votes = proxy({up: 0, down: 0});
        $('h1:'+title);
        $('button:', () => votes.up++);
        $('button:', () => votes.down++);
        $(() => {
          // Do this in a new reactive scope, so changes to votes won't rerun drawThing,
          // which would reset votes.
          $(`:${votes.up} up, and ${votes.down} down`);
          // Of course, we could also call `drawVotes(votes)` instead here.
        })
      }
      
      onEach(['One', 'Two', 'Three'], drawThing);
    • sesm 19 hours ago

      Ok, I see! Thanks!

and0 a day ago

IMO That example is way too complicated to be an elevator pitch. I'm trying to understand what working with your framework would be like for a web app, not the boilerplate to establish rules and state management of a small game.

The tutorial has a much better Hello World + incremental feature introduction, I'd put some of that before the larger example.

  • vanviegen 21 hours ago

    You're right. Updated, thanks!

brap a day ago

You may have something, but your first example is so complex I immediately gave up. Maybe start with something super small like a counter.

almosthere 15 hours ago

It looks influenced by JQuery

  • vanviegen 10 hours ago

    Don't let the $ fool you! :-)

vijaybritto 15 hours ago

Congratulations on the launch!

Have you considered any sort of templating like htm or JSX? Because it's a bit harder to read and I believe to work in a team it would be much simpler if there was a template syntax.

  • vanviegen 10 hours ago

    I admit that it takes some getting used to, but the fact that it's just JavaScript is a feature!

JSR_FDED a day ago

Very interesting. How would you say it compares to mithril.js?

  • lhorie a day ago

    (mithril.js author here)

    As far as I can tell, the main difference is this uses proxies to trigger side effects as opposed to virtual dom diffing. Perf wise, I like that people are pushing the boundaries, but also obviously React is still very popular and that indicates that its level of performance is good enough for a lot of devs </shrug>.

    In terms of features:

    - This was already pointed out in another comment, Aberdeen doesn't seem to be JSX compatible, but it supports similar hyperscript-like syntax (e.g. $('div.foo:hi') vs m('.foo', 'hi'))

    - Unclear what the level of support is for various features that exist in Mithril. e.g. Router appears to be an add-on/example, and I don't see anything about HTTP request utils or SVG/MathML. Mithril is meant to be sort of a one-stop shop for SPAs; it could just be that the focus of the two libraries is slightly different, and that's ok if that's the case.

    - Mithril has hooks to access the raw DOM and uses RAF for batching, it's not clear to me how integration w/ other DOM-touching libs (e.g. dragula) would work here.

    I like the idea of using proxies as a way of implementing reactivity in principle, and I even played around with that idea. IME, there were a couple of things that made it problematic w/ POJO models IMHO. The first is that proxies are not friendly to console.log debugging, and the second is that implementing reactivity in collections (Map/Set/Weak*) was getting a bit too complex for my minimalist tastes.

    • vanviegen a day ago

      Thanks for your input! Although I've never had a good excuse to use it yet, I've glanced at mithril.js quite a few times, and I really appreciate the apparent simplicity and the batteries-included philosophy.

      Indeed, the main difference with Aberdee is in the way change tracking and updates are handled. This is actually a pretty significant, both in terms of internals and in the way you use the framework.

      Fun fact: I once created a another library (for educational purposes) that's a lot more similar to mithril (though not as fully featured): https://www.npmjs.com/package/glasgow

      As I said, I'm a fan of mithril's batteries-included approach. The router is part of Aberdeen, but not imported by default. SVG support is on my todo-list. And I'll be exploring nice ways to integrate with some backends.

      Aberdeen allows (but discourages) you to access the raw DOM as well (through `getParentElement()`), as long as you don't detach/move nodes it created.

      I don't know and can't find what you mean by RAF batching. Aberdeen batches all updates (using a setTimeout 0), and then reruns all dirty scopes in the order that they were created. That also means that parent-scopes go before child-scopes, cancelling the need for separately updating the latter.

      It seems that console.log debugging of proxied objects works pretty well (now, in Chrome at least). It shows that it's a proxied object, and it shows the contents, without subscribing, which is usually what you'd want for debugging. If you do want to subscribe, a `console.log({...myObj})` does the trick. Or use `clone()` (provided by Aberdeen) for recursively subscribing to deep data structures.

      I apparently have the same minimalist taste, having recently removed Map support to simplify things. :-) I'll probably have another go at adding Map/Set/Weak* once I figure out how to do it without with a tiny amount of code. :-)

ederross 16 hours ago

Congrats brother!! Anyone can use it without much knowledge?

gitroom a day ago

honestly super cool you revamped this after all these years - makes me wonder, you think good dev tools are mostly about nailing that feel or does the tech itself even matter long-term

arnorhs a day ago

Congrats on making it to V1!

Though not immediately obvious, there's a lot of parallels to be drawn between this and solid.js (proxy is a sort of `signal` right?, no virtual dom) - I'd be curious what you'd say are the primary benefits of this library over something like Solid.js?

I gotta turn a little negative here...

Off the bat, regarding the first point in the "Why use Aberdeen?" section, I have a few nit-picks:

- "Elegant and simple" - Is it though? Developers should in general be more careful about conflating simple with easy - "Elegant and easy" is probably more accurate, since having a magic proxy function, that will ensure that registered functions will be re-run etc does not constitute "simple" to me.

- "Express UIs naturally in JavaScript/TypeScript, without complex abstractions, build steps, or JSX." - I agree that having an option in the UI library space, where it is no-JSX fist can be a huge benefit in a lot of cases. Personally, these days, given how many ways you can transform and use JSX, I doubt a lot of people feel like that's a huge benefit. And in many ways, it's more natural to express DOM structures as JSX-ish components.

- "No hooks, no setState, no lifting state, no state management libraries." - this is just plain gaslighting. You may not call your API's functions "hooks" and you may very well not call your "proxy()" function a `useState` / `createStore` - but people are still using them the same way... and you end up having to solve all the same issues as with all those libraries

- "Just proxied data and automatically rerunning functions." - this is a big "just"

You are pretending that this library does away with a lot of things that are the bread and bones of other UI libraries but then your UI library still needs all those things.

I'm also curious how your proxy() handles the diamond problem in reactive state, if at all.

--

I have nothing against people building UI libraries for fun - and finding specific use cases where the libraries work well.

Eg. focusing on the lack of build can be a big benefit to a lot of projects / setups that don't perhaps have any kind of build facility etc.. also thinking about "copy pasted scripts" etc - but trying to frame it in a way that it is superior to using something like JSX seems like gaslighting to me.

On a side note, the re: typescript - you do not seem to have strict settings when developing - (from looking at the code examples) - that is already a bit of a red flag and I'd be worried I'd have a lot of TS issues when using the library.

I'll try it out when I get home, and sorry about being so negative.

  • vanviegen a day ago

    Thanks!

    Yes, there's some similarity to Solid.js, but also two big differences:

    1. A Solid.js signal contains a single atomic value that can be subscribed to, while an Aberdeen proxy can wrap around a complex object (containing arrays containing objects, etc), and subscribe to only the primitive values that were read.

    2. If I understand correctly, Solid.js can only redraw entire components (which may not be great at it, like Aberdeen, doesn't use a VDOM), while Aberdeen creates observer scopes every time you nest DOM elements (or manually, if that is not enough for some specific case).

    So Aberdeen is more finegrained on both counts. And there's no need for createMemo and createEffect.

    Re "Elegant and simple": the Aberdeen internals are far from simple, but I think its semantics are very simple to understand. Especially when compared to monsters like React. But I'd settle for easy. :-)

    > "And in many ways, it's more natural to express DOM structures as JSX-ish components."

    Yeah, until you want to add some loops and conditionals in there. That's what regular programming languages are really good at. But it's a trade-off for sure.

    > "this is just plain gaslighting" ... " and you end up having to solve all the same issues as with all those libraries"

    I don't think that's the case. In my (admittedly colored) experience, Aberdeen allows you to do the same things while having to learn far fewer concepts.

    But reading back my text, the claim could have better just said that, as replacing a ton of complex concepts with a ton of different but equally complex concepts is not better. I've updated the site.

    > You are pretending that this library does away with a lot of things that are the bread and bones of other UI libraries but then your UI library still needs all those things.

    My claim is that we don't, in fact, need all those things. As Aberdeen takes a rather different approach, we do of course need some other things (as you pointed out earlier), but I believe this approach allows us to have less of them.

    Re "Diamond problem": I haven't heard the term before, but Aberdeen solves this by a) batching updates (using a setTimeout 0 by default) and b) rerunning all dirtied scopes in the order that they were initially created.

    > but trying to frame it in a way that it is superior to using something like JSX seems like gaslighting to me.

    I'd be happy to add more downsides to the why-NOT-to-use-this list, but my rose colored glasses seem to prevent me from articulating something that makes sense there. :-)

    Re "TypeScript" safety: It says `"strict": true` in the config file. Any other settings you'd want on? Also, the lib has 100% test coverage, if you're into that sort of thing! :-)

    • MrJohz 12 hours ago

      Re 1, SolidJS also has a `createStore` primitive which works the same as your $proxy. That said, based on your other comment that I relied to, it sounds like your internal signals implementation is based on the nested, mutable object case, which is fascinating - I believe most other signals implementations go the other way round, starting with atomic signals and then nesting them to create fine-grained reactivity.

      Re 2, no SolidJS works basically the same as this. For example, in SolidJS,

          <div>my name is {name()}</div>
      
      will transpile to something like (very approximately)

          const el = _render("<div>...");
          const textNode = el.children[1];
      
          createEffect(() => {
            textNode.nodeValue = name();
          });
      
      The render call creates the element once per instance of the component (i.e. not rerendering on ever change), and the `createEffect` is doing roughly what `$(() => { /* */ })` is going - i.e. rerunning the function ever time the called signal changes. This means that when `name()` changes, only one specific node in the DOM will change, and no other work will get done, making the update about as fine-gained as it's possible to get.

      Aberdeen looks like it should be able to do something similar like

          $("div", ":my name is", () => name)
      
      Which I'm guessing would only rerender the "name" node, right? Although I imagine in most cases it's easier to be a bit less fine-gained for the benefit of slightly easier-to-read templates when developing.

      I do like this as a no-build alternative to SolidJS, and I think the syntax works really well for creating mini templates. I can imagine this being great for dropping in little components on traditional server-rendered, mostly static pages - no need for a large library, just something providing the bare essentials in a clean way.

      You mentioned somewhere that in benchmarks, this seemed to be running at around React performance (albeit a lot more lightweight). I suspect you can get a lot more performance by fiddling with the signals implementation. It might even be worth pulling in an existing signals library as a single dependency, or at least exploring how some of the more performant ones are implemented and taking those ideas. Especially as things get more complicated and you end up with more nested `observe`s, getting that to work efficiently is going to be difficult.

      • vanviegen 7 hours ago

        You're right about Solid, of course, I forgot about its compiler doing magic!

          $("div", ":my name is", () => name)
        
        Close.. but $() doesn't act an return values of function arguments. You'd need to do:

          $("div:my name is", () => $(":"+name.value))
        
        Or:

          $("div:my name is", {text: name})
        
        Or, if indeed you don't care about recreating the <div> (which would normally be the case):

          $(`div:my name is ${name.value}`)
        
        Re performance: Aberdeen is already pretty well optimized, so I don't think a lot more could be squeezed out. Aberdeen performs pretty well on js-framework-benchmark, but not spectacular. There's a certain amount of overhead involved in having many fine-grained observers and fine-grained observable. What you gain from that, is a lot more flexibility in where state can live, and not having to worry about things like memoization.

        Where Aberdeen does really shine, performance wise, is reactively displaying lists ordered by some specific function. Other frameworks generally require a sort() and complete list VDOM redraw for each little change, while Aberdeen can just redraw a single row (using a skiplist to determine its (new) position in O(log n)).

    • gg2222 13 hours ago

      Hello, congrats on the release of your framework! I myself also wrote a reactive library for my own projects long ago when jquery was still widely used.

      Anyways, these days I moved from React to Solid.js so I know a bit how Solid works.

      1. Solid.js also has "stores" and "createMutable" which allow deep tracking of objects and are also built on Proxy objects. Signals are great for single values, but Solid stores and createMutable are for more complex data.

      2. Solid.js doesn't redraw entire components. (It's not like React.) It is fine grained and only updates the minimal exact DOM nodes required. This is why it is so fast and topped benchmarks when it first came out. https://dev.to/ryansolid/introducing-the-solidjs-ui-library-...

      I found https://blog.theodo.com/2023/07/solidjs-beginner-virtual-dom... which might be a good intro explanation to it.

      > Yeah, until you want to add some loops and conditionals in there. That's what regular programming languages are really good at. But it's a trade-off for sure.

      Solid's <For> and <Show when={}> and <Switch> tags are actually quite nice and very easy to parse visually.

      Regarding the "gaslighting" comments, I kind of feel the same way as the grandparent. No offense meant and I support everyone coding new open source frameworks, but it does kind of feel like that.

      I suggest doing a deep dive into Solid and even checking Ryan's blog https://dev.to/ryansolid or YouTube channel. There are a ton of concepts and good ideas to learn. He and tanstack are like at the forefront of web dev today.

nsonha a day ago

Isn't that how all UI frameworks work?

ratatoskrt a day ago

[flagged]

  • fender256 a day ago

    As someone who lived there for many years, I would dare to contradict you. There is beauty in the many shades of grey.

    • ratatoskrt a day ago

      There are certainly worse places, and the Shire is gorgeous. But it really doesn't help that it's so far from everwhere else (and I don't just mean London).

    • that_guy_iain a day ago

      When people ask me what it's like to live in Scotland, I tell them: The sky is gray, the buildings are gray, the people are gray. But if you go about 10 minutes in any direction you'll come across nature and it's just green.

      • ratatoskrt a day ago

        At least Edinburgh is brown.

        But seriously, my main problem with Aberdeen is psychological. It feels so far from everywhere else. In Glasgow and Edinburgh, you're much closer to the rest of the UK and even the population centres in Scotland.

        • that_guy_iain a day ago

          There is a reason we call it the central belt for Glasgow and Edinburgh. Everything else is either lowlands or highlands and a bit of a trek.

          • ratatoskrt a day ago

            As someone who moved to the UK from abroad, it was crazy for me to see how unequal the UK is geographically. The main one is obviously the divide between London/everywhere else, but the general lack of infrastructure between places is wild.

            On the other hand, the whole HS2 debacle is a great way to explain this to my friends outside the UK. Bizarre to think it was promised as a way to connect the north of England (and Scotland) to London and the SE...

  • cnity a day ago

    [flagged]

    • ratatoskrt a day ago

      Even worse, I live much further south :)

      I will say that Aberdeenshire is absolutely beautiful though.

    • that_guy_iain a day ago

      Pretty much the entire Scotland has the same joke about Aberdeen.

wackget 17 hours ago

As a web developer of too many years, I just can't get my head around the need for these kinds of reactive libraries.

Re: the first example, the reactive counter.

First of all it mixes content and JavaScript which is a sore subject but personally I prefer to keep the two clearly separated.

Secondly it's a whole bunch of new syntax to achieve what can be done in fewer lines of JavaScript or jQuery and without mixing content with logic:

https://jsfiddle.net/tu1cqh8y/