Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

Vue doing this is awesome, but it isn't really a good reason to prefer it. Server side rendering with progressive enhancement is available in pretty much every major JS framework. Heck, the latest version of React can do SSR with late loading and late binding to make content load even faster. https://github.com/reactwg/react-18/discussions/37


Vue is specifically better because it uses HTML-based templates by default, and HTML is what's produced by web frameworks. This means you can use whatever technology you want on the backend and everything works seamlessly. Also the syntax for directives, handlers, and other logic in these templates is much easier than dealing with JSX in the majority of uses-cases.

SSR/isometric rendering isn't new, but it's usually hacks around using a frontend JS framework to run the backend and can result in the worst of both worlds. Projects like Blazor (.net) and Livewire (php/laravel) are interesting new solutions to fix this.


In a lot of cases it's easier, true, but for other cases it makes things a lot harder. E.g. - how do you conditionally wrap some elements in another element? It's easy in React (assign the subtree to a variable), but impossible to do cleanly in Vue.

That is a general theme, I've found - compared to React, Vue makes the easy things easier, but the hard things harder.


> how do you conditionally wrap some elements in another element ?

You use a dynamic component:

    <component :is="condition ? 'WrappedComponent' : 'UnwrappedComponent"></component>

> That is a general theme, I've found - compared to React, Vue makes the easy things easier, but the hard things harder.

While react make the easy things annoying. It took me an afternoon to be productive with vue, several weeks with react.

And all that for what?

Having the privilege to "just use javascript"? Here is "just javascript" in jsx:

    {items.map((items) => (
        {display && <SomeElement 
        onClick={e => e.preventDefault() && foo()}
        className="`${active ? 'current' : ''}`" />}
    ))}
Here is the same in vue template:

    <SomeElement 
        v-for="item in items"
        v-if="display"
        @click.prevent="foo()"
        :class="{'current': active}" />
Not only the react blob is impossible to scan, but it will be different depending of the dev who coded it, because there is no enforced patterns. You must use brain power to parse every single template line. HTML, that thing that was simple and declarative, now becomes complicated and imperative.

And that's not even weird or unusual code. That's regular stuff one writes all the time. Don't get me started on state management or spaghetti hooks.

I have yet to work on a react code base I was more productive than on a vue one. And the gaps get wider if the team is not composed of excellent coders, because react code by junior is a nightmare.


Well, the fact that you're "just using javascript" in jsx does also mean that you need to format things nicely to avoid having an unreadable mess. Using a code formatter like Prettier helps a lot. The "react blob" is less of a blob if nicely indented.

Admittedly due to the nature of Vue syntax, it's harder to make a mess because everything is just key/value.. but then you have to learn an API to express things in that way. I enjoy the fact that you don't have that layer/step with react.

I definitely feel that the 3rd party classnames package should be built into React, I agree that out of the box conditional class names look a mess.

I don't have enough experience with either library yet to form an opinion on which I prefer, but I definitely appreciate not having the mental overhead of remembering the vue api. I'd rather have a bit of code that's more explicit/"just javascript" than have to think "what's that symbol I need to use again?". Definitely a personal preference thing though!

    {items.map((item) =>
      item.display && (
        <SomeElement
          onClick={(e) => e.preventDefault() && foo()}
          className={classNames({'current': item.active})}
        />
      )
    )}


That's still a blob, just a longer one, with an additional dependency and more parenthesis to mess up. It's a compromise, not an improvement.

And in the end, I still have to read 2 JS expressions before even figure out what DOM element I'm actually dealing with. Then I still have to evaluate what the heck is happening in onClick and className.

I still have to parse code in my presentational layer, code that is interleaved with my template indentation so it also makes it hard to understand hierarchy and easy to make mistakes.

Of course, the elephant in the room we are not talking about with react is that all those things compounds. When you have 5 of such blobs in your render functions, issues don't add up, they multiply.

Then you add the store system on top of it, with actions, reducers and all that fun stuff.


Vue has vuex, so there's a need for actions and reducers. React state has become much simpler with packages like zustand and it works wonderfully with typescript.

It's not really that big of a deal to have another import. Vue's directive's (not sure if that's the correct term) are a mix bag of magic strings that tie to functions somehow. Why can't they just be a function variable like in React? The answer of course is there are smart people and smart users behind both projects and those are the trade-offs we tolerate.


Vuex is vastly less necessary for vue projects than redux for react projects. I've been coding in react and vue for years, it's not even on the same planet: before Vuex we never even needed a store, just import a component, that's your model.

But even so, Vuex way better integrated, requires less boilerplate (thanks vue reactivity) than redux, or even zustand, which once you add immer, looks like blobbing all over again.

Honestly, we should do a contest. Take 10 juniors out of school give them a twitter clone to write, 5 in Vue, 5 in React, and come back in a week.

In fact, no need for them to be junior. Let's just take your regular Java dev. Not your 10X HN dev. Just average Joe. See how it goes.


We've been doing something similar at our company when hiring. The tasks are simple:

1) Build a clone of a site or implement a design with their tool-of-choice (basic competency). For the ones who opt to start without frameworks, we also get a glimpse at how good their organization skills are.

2) After they are done, we give them several "requirement changes" and see how well/fast they adapt. People can trumpet whatever best practices they believe, but this is the part of the interview where some best practices are more practical than others.

3) Modify an existing project. Here, we pick the opposite of their preferred stack/methods. ie, if they prefer vue, we give them a react or svelte app to modify. If they prefer BEM, we give them functional (we use tailwind); if they prefer utility-first, we give them BEM or smacss, etc. While it may seem on-the-spot, it gives us an insight to how well they work outside their comfort zones and adapt to unfamiliar techs.

Our in-house developers are pragmatists over purists, while still being specialists in various techs. And we're fluid with our tech/tools and everyone is at least an expert in one of the current web techs.

And the best thing (imo) about my team is, everyone is excited about new tech. We always try to one-up each other in our various specializations, hence, are also aware of the limitations of our area-of-expertise.

Which comes in handy during interviews. If a candidate says they are an expert in css, we have a resident css expert that can put their claims to the test. You wouldn't believe how many failed candidates proclaim to be a css purist, only to fall short when it comes to organizing and scaling and documentation.


Being so functional and playing so nicely with typescript was the main reason I swayed to react rather than vuejs.

The whole ecosystem of state management does feel like a bit of a wild west.. but I guess that's often the feeling with javascript.

I ended up settling with easy-peasy, which I think has similar aims to zustand. Definitely appreciate that it uses immer out of the box as that seems like a very sensible default way to go.


> <component :is="condition ? 'WrappedComponent' : 'UnwrappedComponent"></component>

That requires you to create 2 new components (which unlike React, will need to be in separate files if you're using SFCs) and pass down all the props another layer.

In the end, I got around it by implementing a "passthrough" component so that you can then do

    <component :is="condition ? 'Wrapper' : 'passthrough'>...</component>
but that is far from ideal.

> Not only the react blob is impossible to scan

Formatted properly, I don't really find the React example any harder to read.

Your Vue example puts v-for and v-if on the same element, which is confusing - which is evaluated first? (IIRC, the answer depends on which version of Vue you are using).

> it will be different depending of the dev who coded it, because there is no enforced patterns

That applies to the rest of your codebase too, though. It's up to your team to decide on patterns and enforce them through linting, code review, and automatic formatting.

Also, both of your examples are missing `key`, though React will warn you about it and Vue will not.


> Also, both of your examples are missing `key`, though React will warn you about it and Vue will not

Vue will warn when you're missing a key on the loop.

>That requires you to create 2 new components (which unlike React, will need to be in separate files if you're using SFCs)

Your components don't need to be in separate files even when using SFC's. Given 'component' can be as simple as adding to any Vue Component or SFC (when adding it to parent component option):

  <script>  
  const myComponent = {
        props: ['myProps'],
        template: `
            <div></div>
        `
        }
  </script>


To be fair, most of use `classnames` and likely have a higher order function for prevent

    import cn from 'classnames'
    const prevent = fn => e => e.preventDefault() && fn()
    
    // don't forget key   
    {items.map(item => 
      display && <SomeElement key="" onClick={prevent(foo)} className={cn(current: active)} />
    )}


Congrats, you are reinventing part of VueJS, in a non standard, untested, undocumented, less readable way. But wait, you need another lambda to pass arguments to foo() should it change. And another wrapper for stopPropagation. And so on, and so on.

And now you have to install and manage deps, import them, while your blob is still less readable because you have maps+lambda+&& to understand before even reaching what DOM element you are manipulating.

The premise of React is that composing primitives is better than having a high level tool to do the job: it's supposed to be more generic, more flexible, easier to learn.

In in my experience, it's true only in the case you don't have a well defined problem. But with frontend UI, we do. We know very well the problem to solve. Give me helpers. Give me wrappers. That's the only case where I welcome a DSL.

I don't want to have to think in functions and workflow in my template.


Changing the lambda?

`onClick={prevent(() => newLambda(what, ever)}}`


I've used both extensively. IMO, complex composition is sometimes easier in React but Vue wins in pretty much everything else. It's almost never in my way.

I'd say Vue is the much more powerful tool in the hands of a skilled developer


The hard things are exactly the same as React because you can use whatever render function you want.

JS itself is a single-threaded event loop and all view engines basically follow the same functional pattern of data-> render function -> output. Vue comes with an HTML parser to create that render function, React and others use a JSX parser, or you can write your own from raw javascript.

You can technically use any of them with any render function parser, but Vue starting with HTML by default and all the other niceties around reactivity make it a better fit.


I know it's just an example, but it's really straightforward: you create a component which accepts a boolean prop and, based on that, either outputs the default slot directly or a wrapper containing the default slot via a simple v-if.

If you wanted to get fancy, you could also accept a tag prop and v-bind all other $attrs on the container to make that a general purpose component.


After building 2 large projects with Livewire (using it just to sprinkle interactivity instead of going for Vue, not being a psycho and building an entire SPA with Livewire), I simply don’t like it. In most cases it would be quicker for me to use jQuery, and the response times would be quicker.


Although Livewire allows you to do that, and shows counters in its examples, for such things that shouldn't require communication with the backend (e.g. saving, or grabbing data from the DB), you are rather meant to use something like Alpine or petite-vue for lightweight JS manipulation than to make use of Livewire's feature for it to waste a roundabout request to the backend.


If you are reaching for Alpine or petite-vue I would consider jquery. The reason why people moved on from it "heavy dom changes" = "slow", "too messy in heavy js pages" is the reason to use it here. Lightweight pages + jquery = fast development, quick to load/render and tidy looking code.


I think the initial marketing/examples for Phoenix Liveview and the like really did a disservice here; when you ask the team always says that the live part should really just be replacing what would have been xhr requests in an SPA - where you need server-side data or functionality. For local interactivity they recommend Alpine but too many people have already seen the counters example and drawn their own conclusions (and even built entire applications this way).


Try unpoly. It's great!


First time I heard of it from you, and I’ve been looking at it for the past day. I’ll definitely try a demo project to see if it fits my needs.


Try Blazor then. It's cutting edge and currently the best implementation possible: https://dotnet.microsoft.com/apps/aspnet/web-apps/blazor


Why are Blazor and Livewire a new solution?

It seems like if you are developing solely to meet "SEO friendly" criteria, you aren't exploring many alternative solutions.

Does all this framework tooling exist to just avoid inspecting a user agent and serving from a different upstream?

Having to maintain a large set of template files, with a dependency on a separate language or framework seems contrary to your premise that Vue is specifically better because it uses "HTML templates by default"


> "developing solely to meet "SEO friendly""

Never said I was. In fact that's just another nice benefit of traditional server-side frameworks rather than something you have to worry about implementing all over again.

Blazor specifically is the first non-JS component-based UI framework for the web with multiple modes that bridges backend and frontend as a single service without any impedance mismatch. It's an entirely new way to approach web UIs and interactivity without all the plumbing and frontend-to-backend-for-the-frontend-to-the-backend nonsense. Livewire is not quite as polished but shows how far you can go with tight integration on the JS side.


Vue templates are far worse than JSX. JSX/TSX is understood by tools so you get static typing, code completion, go-to-definition and all the other benefits of modern tooling.

Static typing alone is enough to rule out Vue in my opinion. I even went as far as writing a static type checker for Vue to work around it. It works by compiling the Vue template to a dummy Typescript function that only typechecks if your Vue types are correct.

It found loads of bugs in our code.


That's just a tooling issue. HTML has been around for a long time, and plenty of tools provide typing. Also Vue supports typescript. Vue itself throws errors if the variables don't exist in your template.

However you can use JSX with Vue if you want. All JS libraries are fundamentally the same, and all templating boils to a single function with input->output. Vue just offers HTML instead of JSX by default and it's far more useful in many instances.


> That's just a tooling issue.

No it isn't. There isn't even a way to write down the types properly in Vue templates.

Anyway just saying "it's a tooling issue" doesn't make it not an issue!

> Also Vue supports typescript.

"supports" is a scale, and the support isn't nearly as good as React/TSX.


Vue 3 is written in typescript and comes with full definitions. The composition API is all regular functions. It doesn’t get better supported than that.


Yes it does. React has better support!


We don't write code for the tools. We use tools to write better code.

Simple eh?


Tools help us write better code. If a library doesn't let us use tool then our code will be worse.

Simple eh?


Nah hermano. Bad code isn't a tools fault.

That's just down to a lack of skill and poor decision making in terms of tools to use for the right job.

Don't blame a knife for cutting your finger, don't blame the coffee table for yiur stubbed toe, don't blame tools for your bad code.


> Server side rendering with progressive enhancement is available in pretty much every major JS framework

But then you are forced to use Javascript on the server side instead of whatever language you are already using. That seems to be a pretty good reason for using Vue as a tool for "progressive enhancement" to me.




Consider applying for YC's Summer 2026 batch! Applications are open till May 4

Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: