If you check out some of my previous posts, you'll know that I've been doing some experiments with Svelte and Sapper lately.
As I've been working with Angular for years and now I'm learning Svelte by doing, I thought it could be useful to migrate some of my components from Angular to Svelte.
In this article, I'll share the following card component built both with Angular and Svelte so you can see the differences:
I'm using tailwindcss as my utility-first CSS framework. If you're not familiar with tailwind, please checkout the official tailwindcss site. Also, if you want to start using tailwindcss with angular or with Svelte, I wrote the following articles:
Angular
For the Angular version of this card component, we'll need to create a post-card component like this:
// post-card.component.ts
import { Component, Input } from '@angular/core'
@Component({
selector: 'post-card',
templateUrl: './post-card.component.html',
styleUrls: ['./post-card.component.css']
})
export class PostCardComponent {
@Input() title;
@Input() description;
@Input() location;
@Input() picUrl = 'https://i1.wp.com/www.foot.com/wp-content/uploads/2017/03/placeholder.gif?ssl=1'
@Input() createdAt;
@Input() labels = [];
constructor() { }
showMore() {
alert("showing more...")
}
}
- The @Input() decorator allow us to share data between components. An @Input() property is writable while an @Output() property is observable.
<!-- post-card.component.html -->
<div class="flex flex-wrap shadow-lg rounded-lg overflow-hidden mb-6">
<!-- Image -->
<div class="w-full h-48 md:h-auto md:w-1/4 bg-cover bg-center" [style.backgroundImage]="'url('+ picUrl +')'"></div>
<!-- Details -->
<div class="w-full md:w-3/4 px-6 py-4 bg-white">
<div class="py-2">
<p class="text-2xl">{{title}}</p>
<p class="text-sm text-gray-600 mb-2">{{createdAt | date:'MM/dd/yyyy'}}</p>
<!-- Labels -->
<div class="flex flex-wrap">
<p *ngFor="let label of labels"
class="border border-solid border-orange-500 rounded w-auto inline-block px-2 py-1 mr-3 text-sm mb-2">
{{label}}
</p>
</div>
</div>
<p class="flex items-center text-sm mb-4">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24">
<path class="heroicon-ui"
d="M4.06 13a8 8 0 0 0 5.18 6.51A18.5 18.5 0 0 1 8.02 13H4.06zm0-2h3.96a18.5 18.5 0 0 1 1.22-6.51A8 8 0 0 0 4.06 11zm15.88 0a8 8 0 0 0-5.18-6.51A18.5 18.5 0 0 1 15.98 11h3.96zm0 2h-3.96a18.5 18.5 0 0 1-1.22 6.51A8 8 0 0 0 19.94 13zm-9.92 0c.16 3.95 1.23 7 1.98 7s1.82-3.05 1.98-7h-3.96zm0-2h3.96c-.16-3.95-1.23-7-1.98-7s-1.82 3.05-1.98 7zM12 22a10 10 0 1 1 0-20 10 10 0 0 1 0 20z" />
</svg>
<span class="ml-2">{{location}}</span>
</p>
<p class="mb-4">{{description}}</p>
<div class="flex flex-wrap md:flex-row-reverse">
<button (click)="showMore()"
class="w-full md:ml-2 px-4 py-3 rounded bg-orange-600 hover:bg-orange-500 mb-2 md:w-auto md:mb-0 text-white">
Show more
</button>
</div>
</div>
</div>
- I'm using pipes to change the date format. A pipe takes in data as input and transforms it to a desired output. In this case, I'm transforming a javascript date to 'MM/dd/yyyy'.
- Data binding is achieved by using double curly braces like this:
{{yourProperty}}
- To setup an image as a background-image within the card, I'm using property binding like this:
[style.backgroundImage]="'url('+ picUrl +')'"
- NgFor is a structural directive that renders a template for each item in a collection. In this case, I'm using it to iterate through each label like this:
<p *ngFor="let label of labels" class="border border-solid border-orange-500 rounded w-auto inline-block px-2 py-1 mr-3 text-sm mb-2">
{{label}}
</p>
Usage
In order to display our post-card component, I'm adding a card property in the AppComponent like this:
// app.component.ts
import { Component } from '@angular/core'
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
// This data will be sent to our post-card component
card = {
title: "Lorem ipsum dolor sit amet, consectetur adipiscing elit",
description: "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum",
location: "New York",
picUrl: "https://images.pexels.com/photos/1060803/pexels-photo-1060803.jpeg?auto=compress&cs=tinysrgb&dpr=2&h=750&w=1260",
createdAt: new Date(),
labels: ["Travel", "People"]
}
constructor() { }
}
}
<!-- app.component.html -->
<post-card [title]="card.title"
[description]="card.description"
[location]="card.location"
[picUrl]="card.picUrl"
[createdAt]="card.createdAt"
[labels]="card.labels">
</post-card>
- This time, I'm using property binding to send data from AppComponent to PostCardComponent.
Svelte
Now, let's use Svelte to build exactly the same component
<!-- PostCard.svelte -->
<script>
export let title
export let description
export let location
export let picUrl = 'https://i1.wp.com/www.foot.com/wp-content/uploads/2017/03/placeholder.gif?ssl=1'
export let createdAt
export let labels = []
function showMore() {
alert("Showing more...")
}
</script>
<div class="flex flex-wrap shadow-lg rounded-lg overflow-hidden mb-6">
<!-- Image -->
<div class="w-full h-48 md:h-auto md:w-1/4 bg-cover bg-center" style="background-image: url('{picUrl}')"></div>
<!-- Details -->
<div class="w-full md:w-3/4 px-6 py-4 bg-white">
<div class="py-2">
<p class="text-2xl">{title}</p>
<p class="text-sm text-gray-600 mb-2">{createdAt.toLocaleDateString()}</p>
<!-- Labels -->
<div class="flex flex-wrap">
{#each labels as label}
<p
class="border border-solid border-orange-500 rounded w-auto inline-block px-2 py-1 mr-3 text-sm mb-2">
{label}
</p>
{/each}
</div>
</div>
<p class="flex items-center text-sm mb-4">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24"><path class="heroicon-ui" d="M4.06 13a8 8 0 0 0 5.18 6.51A18.5 18.5 0 0 1 8.02 13H4.06zm0-2h3.96a18.5 18.5 0 0 1 1.22-6.51A8 8 0 0 0 4.06 11zm15.88 0a8 8 0 0 0-5.18-6.51A18.5 18.5 0 0 1 15.98 11h3.96zm0 2h-3.96a18.5 18.5 0 0 1-1.22 6.51A8 8 0 0 0 19.94 13zm-9.92 0c.16 3.95 1.23 7 1.98 7s1.82-3.05 1.98-7h-3.96zm0-2h3.96c-.16-3.95-1.23-7-1.98-7s-1.82 3.05-1.98 7zM12 22a10 10 0 1 1 0-20 10 10 0 0 1 0 20z"/></svg>
<span class="ml-2">{location}</span>
</p>
<p class="mb-4">{description}</p>
<div class="flex flex-wrap md:flex-row-reverse">
<button
on:click={showMore}
class="w-full md:ml-2 px-4 py-3 rounded bg-orange-600 hover:bg-orange-500 mb-2 md:w-auto md:mb-0 text-white">
Show more
</button>
</div>
</div>
</div>
- Instead of using pipes, I'm formatting the javascript Date using
{createdAt.toLocaleDateString()}
. - Data binding is achieved by using single curly braces like this:
{yourProperty}
- To setup an image as a background-image within the card, I'm using data binding like this:
style="background-image: url('{picUrl}')"
- Instead of using structural directives for looping through items, Svelte use each blocks:
{#each labels as label}
<p class="border border-solid border-orange-500 rounded w-auto inline-block px-2 py-1 mr-3 text-sm mb-2">
{label}
</p>
{/each}
Usage
<!-- OtherSvelteComponent.Svelte -->
<script>
import PostCard from "../components/PostCard.svelte";
// This data will be sent to our post-card component
const card = {
title: "Lorem ipsum dolor sit amet, consectetur adipiscing elit",
description: "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum",
location: "New York",
picUrl: "https://images.pexels.com/photos/1060803/pexels-photo-1060803.jpeg?auto=compress&cs=tinysrgb&dpr=2&h=750&w=1260",
createdAt: new Date(),
labels: ["Travel", "People"]
}
</script>
<PostCard {...card} />
Final thoughts
As you probably noticed, the Svelte implementation is smaller. To be more precise, it's 20% smaller: 2348 characters (Svelte) against 2817 (Angular).
You may think that 20% isn't a big difference, but the smaller the components are, the greater will be the difference between both implementations.
To prove that point, let's see a super basic example:
// Angular - HelloWorld component
import { Component } from '@angular/core';
@Component({
selector: 'hello-world',
template: '<p>{{message}}</p>',
styleUrls: ['./hello-world.component.css']
})
export class HelloWorldComponent {
message = "Hello world"
constructor() { }
}
<!-- Svelte - HelloWorld component -->
<script>
let message = "Hello world"
</script>
<p>{message}</p>
In this case, the Svelte component is 400% smaller: 224 characters (Angular) against 59. And that's the added value! By keeping your components as small and atomic as possible, you'll be writing much less code.
What do you think about Svelte? Have you tried it yet?
Let me know in the comments below!
Top comments (20)
Counting characters to compare a fairly simple components is way too naive as a comparison: I do like both Angular and Svelte, so I don't think I am biased when I say this. I'd like to see the comparison of two full-blown complex, real world apps. Only then we can probably compare them.
Well, as someone who's only familiar with Angular, I can say that I find these comparisons interesting and useful.
But for folks like you, there's a website delivering exactly what you're asked for: github.com/gothinkster/realworld
I agree: they're useful, I made one myself, but I wouldn't use counting characters as a metric for comparison. There's so much more in an application than components, which in my opinion are the "easy" part. The real world app you link is hardly one: I'm talking enterprise, resilient, rock-solid stuff. Probably not something you can find in the open, I admit.
I think your point is valid, but not all the apps are enterprise or super complex. And, for those kind of apps it's helpful to see this kind of comparisons. Btw, I love Angular and I'm using it on most of my projects.
As I said on previous posts, I'm just starting with Svelte and Sapper. Maybe in the feature I'll be comparing more complex stuff, but this series are all about components.
They are probably the easy part, but they are also a big part of the code we write every date.
Glad it was useful for you! 😄
As I said in another comment, that's hardly a real world app. A more complex todo app at best.
I m a react developer .. i kicked React in the trash because of Svelte .. i love it 😍.. Svelte became my new girlfriend 😂😂
Id like to do the same but the lack of good ts support made us break up
Did you try svelte with sapper?
I'd just like to add, that it took me about 9 days to do one website with React back in December 2019. I was able to refactor the entire website in Svelte in less than a day. Obviously refactoring an existing site is alot easier than building from scratch, but I mention this to illustrate the ease with which you can transition. The new Svelte site used 63% of the code that React did, and my Google PageSpeed score for that site went from 87 (mobile) to 99 (mobile). For web projects, I LOVE Svelte.
How does svelte handle css?
U just use a
<style>
tag (and it scopes it automatically)Nice. Does it support separate files? If that's your thing
Tbh i have no idea
It does. css-tricks.com/what-i-like-about-w...
To make it more fair should not
styleUrls: ['./hello-world.component.css']
be removed?Svelte is superior and a better saner approach with more future-proof concept.
I prefer Nim tho.
I love that way of developing man.
Thanks! 😄