Meteor + Svelte

In case you haven’t heard about Svelte: it’s a new UI library that compiles components consisting of HTML, CSS, and JavaScript to small JavaScript modules that don’t need a runtime.

You can now use Svelte in Meteor with meteor-svelte!

To use it in a new Meteor app, run meteor add svelte:compiler and meteor remove blaze-html-templates. The build plugin compiles Svelte components (files with .html extension) to JavaScript modules that can be imported anywhere in your app.

Here is a simple example of how you would write a component that retrieves data from Minimongo and displays it (alternatively, you can use the svelte:tracker package):

<!--PersonComponent.html-->

<div>
  {{#if person}}
    {{person.firstName}} lives in {{person.city}}
  {{/if}}
</div>

<script>
  import { Meteor } from 'meteor/meteor';
  import { Tracker } from 'meteor/tracker';

  // Document structure:
  // {
  //   _id: …,
  //   firstName: …,
  //   city: …
  // }
  import Persons from './persons.js';

  export default {
    onrender() {
      this.computation = Tracker.autorun(() => {
        Meteor.subscribe('persons');
        this.set({ person: Persons.findOne() });
      });
    },

    onteardown() {
      this.computation.stop();
    }
  };
</script>

Render the component into the DOM like this:

import PersonComponent from './PersonComponent.html';

Meteor.startup(() => {
  const component = new PersonComponent({
    target: document.querySelector('#svelte-root');
  });
});
9 Likes

Beautiful! Is it reactive?

1 Like

The package doesn’t provide a Tracker integration like react-meteor-data but it’s easy to make components reactive by using Tracker directly (as in the example above). All you need to do is fetch data inside an autorun and set the component’s state with this.set when new data arrives.

I first heard about Svelte a few days ago via the latest Transmission podcast. @sashko mentioned it briefly as a cool idea, and now we already have a package integrating it with Meteor. Man, I flippin’ love this community! :heart:

6 Likes

OMG YES!

I am going to try this ASAP, was too lazy to set up the right build scripts before.

3 Likes

I think the Svelte approach doesn’t really make sense. I wonder if the resulting code for a large application will be smaller than a normal library+user code.
In the end, a framework mainly implements patterns that often recur. Svelte can’t make this abstraction. so you will have a lot of duplicate logic throughout your code as it grows.

1 Like

Oh sure, I don’t know if it’s the most practical way to build large apps today, but if you add code splitting it could be really good to only actually load the code you are using.

Seems like this problem is already known, and there are different issues 1, 2 to try to reduce the size of the code that is produced by grouping common helpers/ methods. I would say even with some duplication, Svelte is still interesting, since it’s keen on code splitting, so you could serve only what’s needed at any time.

Yeah I also think it’s important to remember that no single library can be the best for all cases - it’s good to have a few tools in your toolbox, and Svelte can be a great one! I really like the concept that the smallest app in Svelte is 0 bytes, rather than a bunch of preloaded framework code.

This is great, it’s always good to see a new view layer option.

I would suggest that you do something similar to what Vue does with its build system, where you use a custom file extension like .svt. Vue component files have a .vue extension and can contain HTML, CSS and JS, which are build into a JS compnent. Since this is s a file for a JS component that includes JS logic (with a required interface to adhere to) as well as an HTML view and scoped CSS, I think giving it its own file extension would make sense.

Then you also wouldn’t need to do a kind of hacky

 if (el.name === 'head' || el.name === 'body') {
        isSvelteComponent = false;
}

Btw I like your example of how to set up Tracker data, and I think it could be added to your build plugin automatically. That way a user could do something like

<script>
export default {
    tracker() { 
        return { 
           posts: Posts.find({})
        }
    },
   onrender() {
      alert('render');
   },
   onteardown() {
      alert('teardown'); 
   }
}
</script>

And your build plugin could build it into this

<script>
export default {
    onrender() {
         alert('render');
         
          // this is auto-generated by your plugin
         this.posts = Tracker.autorun(() => {
            this.set({ posts: Posts.find({}) })
         });
    },

    onteardown() {
        alert('teardown');

       // this is auto-generated by your plugin
       this.posts.stop();
   }
}
</script>

This wouldn’t be too hard tbh. You can extract the content of the script tag and modfiy it like this here when you have the raw file

const raw = file.getContentsAsString();
const rawScript = raw.match(/<script>(.*?)<\/script>/);

const rawScriptExportedObject = rawScript.replace('export default', ''); 

let scriptExportedObject = eval(rawScriptExportedObject);
let scriptTrackerProperty = scriptExportedObject.tracker;

At that point you’ve the the exported .tracker property as scriptTrackerProperty, and you could roll with that and automatically modify the exported object, then insert it back into the raw file string before using svelte to compile it. (I didn’t test this, so idk if it works but you get the idea)

I probably won’t use svelte myself so I’m not going to put together a pull request for it, but I think you could do this really easily!

Thank you for these suggestions, @efrancis. I also thought about using a different file extension but opted for .html because that’s what the author of Svelte uses.

For a similar reason I didn’t add a Tracker integration that modifies the code. If you add a tracker property to the exported object, it’s not a valid Svelte component anymore because the compiler accepts only predefined properties such as onrender and data.

However, I agree that it would be useful to have an abstraction for the autorun. What do you think about this?

import { withTracker } from 'svelte-tracker';
import PersonComponent from './PersonComponent.html';

export default withTracker(() => {
  Meteor.subscribe('persons');
  this.set({ person: Persons.findOne() });
}, PersonComponent);

The withTracker function would create a new component that wraps the PersonComponent, similar to createContainer for React.

2 Likes

From the guy who created it on a Hacker News thread:

The biggest problem with web apps today is the initial load time, not the total size of the app. If you can’t render the first page the user lands on without serving a large framework, you’re stuck.
But yes, an app built entirely out of standalone components would eventually overtake the total size of an app built using a more conventional framework. (By the time you get there, your app is probably already too big anyway, and you should be code-splitting.) We’re going to add a compiler mode that addresses that very soon by deduping some code within an app.

https://news.ycombinator.com/item?id=13069841

But there are already a lot of PWA techniques for solving the initial load problem. + a compiler that dedupes common code sounds very much like you ending up with a library anyway.

Don’t get me wrong. It’s cool to see people trying new techniques. But I’m much more interested in a framework that allows me to implement my business logic and as good as possible instead of a framework focused on one specific performance aspect. Because the latter is way too much of a moving target.

1 Like

I was talking about making your build plugin remove the tracker property from the raw file content and changing it to look like your example (using Tracker inside onrender), before you pass the file content into the svelte compile function, so it would be valid and parseable. I like your idea of a decorator though, that would be easier to implement as well.

Finally, nice to see you guys like it!

Rich Harris does Ractive.js framework and Rollup.js, you trust he know what he is good at, absolutely easier to resolve code duplication. At the end, it’s lot faster to parse native Javascript than VDOM.

Angular was awfully slow response by a few seconds with a large web application. Nothing need more than 1 second, that why Svelte has lowest overhead/penalty, easier for any Javascript engine including embedded devices to run without frameworks I guess. iOS vs Android responsive race for example.

If you need more info, chat with us at https://gitter.im/sveltejs/svelte

Just like the rest of your post, this statement doesn’t make any sense.

  1. There’s no such thing as native javascript, there’s just javascript. VDOM implemented in javascript is… javascript.
  2. Javascript parsing time is more or less constant for similar file sizes. It’s not like the parser knows it’s parsing a VDOM library and there are already VDOM libraries of just a few KB.

I do think however that there’s a value in Svelte, in that you can create shareable components.
You can create a component and anyone can use it, no matter what library they use. However, that’s just a very temporary advantage until web components are widely supported.

Sorry if it didn’t make sense, let me share one article which should explain it clearly on SSR and CSR but there are a few more comprehensive comparison I can’t remember which links.

Aerotwist is infamous for disliking any framework. But more important, all his findings would just as much apply to svelte. Because it’s not about frameworks, but it’s about how components are bootstrapped.