Skip to content

Instantly share code, notes, and snippets.

@Rich-Harris
Last active December 20, 2019 15:19
Show Gist options
  • Star 4 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save Rich-Harris/5b4edf7adb97d4f6b48778ff37366eb3 to your computer and use it in GitHub Desktop.
Save Rich-Harris/5b4edf7adb97d4f6b48778ff37366eb3 to your computer and use it in GitHub Desktop.
Source bytes: React vs Ember Octane vs Svelte

I'm procrastinating instead of working on a conference talk, so I decided to implement this component in Svelte to see which framework is most expressive:

Screen Shot 2019-03-30 at 16 42 16

Ember Octane

Here's the original, with the addition of an import for dataUriAsSrc (and updated per @chriskrycho's comment):

<img
  class='rounded-full object-cover'
  src={{this.src}}
  alt="Profile image for {{@name}}"
  height={{this.size}}
  width={{this.size}}
  onerror={{this.useLetterform}}
>
import Component from '@glimmer/component';
import { tracked } from '@glimmer/tracking';
import { action } from '@ember/object';
import dataUriAsSrc from './dataUriAsSrc.js';

export default class AvatarComponent extends Component {
  size = 48;
  @tracked letterform;

  get src() {
    return this.letterform
      ? this.letterform
      : `https://example.com/avatars/${this.args.companyId}`;
  }

  @action useLetterform() {
    this.letterform = dataUriAsSrc(this.args.name, this.size);
  }
}

Svelte

Here's a Svelte version (demo):

<script>
  import dataUriAsSrc from './dataUriAsSrc.js';

  export let size = 48;
  export let name;
  export let companyId;

  let letterform = false;

  $: src = letterform
    ? dataUriAsSrc(name, size)
    : `https://example.com/avatars/${companyId}`;
</script>

<img
  class="rounded-full object-cover"
  alt="Profile image for {name}"
  src={src}
  width={size}
  height={size}
  on:error={() => letterform = true}
>

React

And here's one using React hooks (demo):

import React, { useState, useMemo } from "react";
import dataUriAsSrc from "./dataUriAsSrc.js";

export default function Avatar({ size = 48, name, companyId }) {
  const [letterform, setLetterform] = useState(false);

  const src = useMemo(
    () => letterform
      ? dataUriAsSrc(name, size)
      : `https://example.com/avatars/${companyId}`,
    [letterform, name, companyId, size]
  );

  return (
    <img
      className="rounded-full object-cover"
      alt={`Profile image for ${name}`}
      src={src}
      width={size}
      height={size}
      onError={() => setLetterform(true)}
    />
  );
}

They're all pretty sleek β€” much more so than frameworks of yore. I find it useful to compare both lines of source code and bytes of source code (to count bytes, copy to clipboard then run pbpaste | wc -c in your terminal):

framework lines of code bytes of code
Svelte 22 422
React 24 607 (+44%)
Ember 27 673 (+59%)

In summary: use Svelte 😜

@chriskrycho
Copy link

chriskrycho commented Mar 30, 2019

Edited to clean up 2019/10/21.

If we're going for that nice compressed style that the React and Svelte ones have (which I admit to liking a lot) then the Octane component should look like this (674 570 bytes):

import Component from '@glimmer/component';
import { tracked } from '@glimmer/tracking';
import dataUriAsSrc from './dataUriAsSrc.js';

export default class AvatarComponent extends Component {
  @tracked letterform = false;

  get src() {
    return this.letterform
      ? dataUriAsSrc(this.args.name, this.args.size)
      : `https://example.com/avatars/${this.args.companyId}`;
  }
}
<img
  class='rounded-full object-cover'
  src={{this.src}}
  alt="Profile image for {{@name}}"
  height={{@size}}
  width={{@size}}
  {{on "error" (fn (mut this.letterform) true)}}
>

Using the experimental set helper:

<img
  class='rounded-full object-cover'
  src={{this.src}}
  alt="Profile image for {{@name}}"
  height={{@size}}
  width={{@size}}
  {{on "error" (set this.letterform true)}}
>

Drops it by another 5. πŸŽ‰ (Also, and more importantly, reads much more nicely.)

@Rich-Harris
Copy link
Author

Thanks! I've updated the gist

@hoIIer
Copy link

hoIIer commented May 28, 2019

the ember code can be further reduced to 23 loc, regardless thx for comparison and great work on svelte

import Component from '@glimmer/component';
import { tracked } from '@glimmer/tracking';
import { action } from '@ember/object';
import dataUriAsSrc from './dataUriAsSrc.js';

export default class AvatarComponent extends Component {
  size = 48;
  @tracked letterform;

  get src() {
    return !!this.letterform
      ? dataUriAsSrc(this.name, this.size);
      : `https://example.com/avatars/${this.companyId}`;
  }
}
<img
  class='rounded-full object-cover'
  src={{this.src}}
  alt="Profile image for {{@name}}"
  height={{this.size}}
  width={{this.size}}
  onerror={{action (mut useLetterform) true}}
>

@enoh-barbu
Copy link

That's not how one should compare libraries or frameworks, looking only at the smaller bits, but a real deal comparison would be at a large scale, there's where performance, scalability and others will make a decision.

@liyuanqiu
Copy link

Comparing frameworks in all aspects often means comparing nothing πŸ˜„πŸ˜„πŸ˜„
That's why no one can say React is better than Vue or vice versa.
Choose the one your heart chooses...

@enoh-barbu
Copy link

It's not like that, programming it's related to computers, not hearts or souls

@Sonos21
Copy link

Sonos21 commented Dec 5, 2019

I hope 'hearts and souls' will still be writing the programs for a long time to come....
but i understand that will open another discussion :-)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment