features · Sep 3, 2014

Quire: Building a Large Application with Dart

My helpful screenshot

Last updated: May 28, 2026

TL;DR: Quire (53,992 lines, 1,620 KB of Dart) was built in 2014 with Dart on both client and server, using DQuery and Bootjack instead of AngularDART or Polymer.dart for finer DOM control. The core decisions (strong typing across the stack, shared client-server data models, event loop on the server, and tree-shaking for JS output) still hold up in 2026.

Quire is not the first web application written in Dart and won’t be the last one, but it could be the first app that relies on Dart so heavily, on both client and server sides.

My helpful screenshot

It’s a fun task manager with tree structure to the core. The project itself has 53992 lines, 1620KB of Dart code, built with a few open source libraries from the community.

Who built Quire?

Before we started to work on this project, we are Dart enthusiasts known as the Rikulo team, who have released several Dart libraies, including UI framework, UI library, web server, messaging server, database client, etc.

We were all excited by Dart’s future worth when Dart was first released in 2011, and right away we started to work with Dart in small fractions of projects. Eventually we moved on to build a full application that consists of Dart majorly. Here we would like to humbly shared some of our experience, hoping to give you some information about developing in Dart.

Why did Quire choose Dart over JavaScript?

There are tons of reasons to choose Dart. Among them, the most important ones for us are:

  • Dart’s strong type system guards us from countless tiny mistakes. Dart Editor, the official IDE, gives quick feedbacks on type errors and auto-completions as well as easier tracing.
  • We think the class-based inheritance model is much more intuitive than the prototype-based one.
  • Dart offers the chance to use the same language on both client and server side, sharing the same data model as well as code base.
  • Dart fixes most of the gotchas in JavaScript. Dart isn’t perfect on this, but it does take care of 99% of the old bad surprises.
  • Dart has a few popular JavaScript Harmony features available today: Future (promise), Arrow Function, etc.
  • Dart is backed up by a strong team, with many high quality official libraries and well organized APIs. Dart is still evolving but the specification is quite stable already.
  • On server side, we prefer event loop model over multi-threading.
  • Dart performs tree shaking when compiled to JavaScript. (See below.)

There are also cons of choosing Dart:

  • Dart community is way smaller than JavaScript community.
  • Communication across Dart and JavaScript isn’t trivial.
  • It’s harder to polyfill APIs in a strong typed language.
  • Dart Editor performance is not optimal for large project (yet).

Embraced by strong type support, writing in Dart is much more reliable than in JavaScript. Also, Dart is less verbose than Java, and sometimes (for example, with function expression) even less verbose than vanilla - - JavaScript. In general, working with Dart is comfortable, except for a few things:

  • Final field must be initialized at initializer, eariler than constructor body.
  • Mixin specification is rather immature, and it won’t be fixed before 2.0.
  • No generic type parameter for function declaration, only on class. (Of course, this will increase the workload of compiler.)

How does Dart work on the client side?

Before Dart VM lands on Chrome, you would expect to deliver your product by compiling it to JavaScript. There are tons of languages which compile to JavaScript, but Dart stands out with a few extra advantages.

  • In development, we run Dart natively on Dartium, a browser forked from Chormium with a built in Dart VM. We don’t need to compile in this iteration, exempting us from countless interuptions.
  • In tests and production, we compile them to JavaScript and run with all major browsers. The Dart-to-JavaScript compiler performs tree shaking, which trims off unused code from the outcome. This greatly reduces our JavaScript code size.

Why use Dart on the server side?

Dart on server side was never a hot topic in the Dart community, but we consider Dart as a strong candidate for server side programming language, because:

  • Web service, by its asynchronous nature, works very well with event loop model (over multi-threading).
  • Server side code demands much more robustness and security than client. A strong-typed language really comes in handy on this.

What libraries does Quire use in its Dart stack?

Quire is built with around 30 library imports, and of which 10 come from the community, while the rest are released by Dart team. While you may guess there are AngularDART and Polymer.dart if you are a Dart expert, they are actually both absent.

We are not using AngularDART, because:

  • We want delicate DOM control.
  • We use unique architectural guidelines to design the client structure, and the paradigm is different from the Angular logic.
  • When we surveyed AngularDART it generated a large overhead on compiled JavaScript code size, but this has been greatly improved.

We are not using Polymer.dart either, because:

  • Due to encapsulation and event re-targeting, ShadowDOM can’t collaborate with selector oriented framework, for example, Bootstrap.
  • You can’t inject style from the outside of ShadowDOM. If a 3rd party component set is made of Polymer, it’s almost impossible for users to change its look-and-feel. Updated: As of Dec 2013, you can modify internal style from the outside. See Shadow DOM 201.

What are DQuery and Bootjack?

The cornerstones of our client side stack are DQuery and Bootjack, which are both open source projects released by the Rikulo team:

  • DQuery is a partial porting of jQuery to Dart, focusing on the event delegation system of jQuery.
  • Bootjack is a complete porting of Bootstrap 3, with almost identical CSS and APIs.

We built our application stack along this way to use our knowledge and skills from the JavaScript world.

What is the Stream web server?

Stream is our web server, written in pure Dart, equipped with the abilities of routing, filter, server side MVC, etc. Stream works smoothly with the event loop model. Writing a request handler is just about chaining a sequence of non-blocking routines. It is not only more productive and also more pleasure to work with than traditional multi-threading model. We also scale our web service and handle HTTPS with nginx, and delegate requests to Stream. With this architecture we are able to spawn and despawn Dart VM individually and upgrade the server without interupting user activities.

Conclusion

We have an enjoyable experience working with Dart, and we are looking forward to more exciting developments in the Dart community. Last but not least, if you are curious about what you can achieve with Dart, come and play with our app, Quire. It’s free!

My helpful screenshot

What lessons from this 2014 build still apply in 2026?

The Dart language and the Quire codebase have both evolved significantly since this post was written. Dart 2.0 (sound type system) landed in 2018, Dart 3.0 (null safety, records, patterns) in 2023, and Flutter changed the cross-platform UI landscape. But the underlying architectural decisions documented above hold up better than you might expect.

Strong typing across client and server still pays off. The runtime errors a strong type system catches at build time are still the same runtime errors. JavaScript moved partway toward typing through TypeScript, but a fully typed end-to-end stack remains rare. The class of bugs Simon called out (countless tiny mistakes) are still the class of bugs an untyped stack ships to production.

Shared data models between client and server still cut work. The duplicate-model problem (one in JS, one in Python, drifting silently) is the same in 2026 as it was in 2014. The fix (one language end-to-end) is also the same. TypeScript-on-Node teams build Quire-style architectures for exactly this reason.

Event loop over multi-threading still wins for web servers. Node, Deno, Bun, and Dart's own Stream pattern all agree on this. The argument has been re-litigated every few years and lands in the same place.

Tree shaking matters more, not less. In 2014, Dart's tree-shaking compiler was a differentiator. In 2026, it's table stakes for any JS-targeting build pipeline. Webpack, Rollup, esbuild, and Vite all do it. The lesson holds; the tooling caught up.

ShadowDOM concerns from 2014 mostly resolved. Polymer.dart was deprecated. Web Components matured. The reasons Simon gave for avoiding Polymer (style injection, framework collaboration) were eventually addressed by the platform. The general principle (avoid bleeding-edge frameworks for production app cores) still applies in 2026 to whatever the current "exciting" framework is.

What are the most common mistakes when choosing a language for a large web app?

Picking the language stack for a multi-year application is one of the few engineering decisions that's expensive to reverse. Four patterns recur when teams get it wrong.

1. Choosing the language with the largest community by default. JavaScript has the largest community, but "largest" is not the same as "best fit for your problem." A language with a strong type system, good tooling, and a smaller community can outperform a sprawling ecosystem with weak guarantees. Community size matters, but it isn't the only thing that matters.

2. Picking a stack that splits client and server languages. Every duplicated data model is a bug waiting to happen. Teams that ship Ruby on the server and JS on the client carry that cost forever. Teams that share one language across both surfaces (Dart, TypeScript, Kotlin) avoid an entire bug class.

3. Adopting the bleeding edge before it stabilizes. Quire's team avoided AngularDART and Polymer.dart in 2014 because both had open architectural questions. AngularDART pivoted hard and Polymer was deprecated. The general rule (let a framework prove itself in production at scale before betting your app core on it) still applies in 2026 to whatever the current "exciting" framework is.

4. Skipping the type system for short-term speed. Untyped languages feel faster in week one. By month six, the time saved is more than refunded in debugging time. Engineering teams that have lived through both untyped and typed stacks at scale almost never go back voluntarily.

The Quire team's 2014 bet on Dart looks reasonable in hindsight not because Dart became dominant (it didn't) but because the underlying criteria (typed, shared client-server, event loop, tree shaking) became industry consensus over the following decade. The decisions that aged well were the underlying engineering principles, not the specific tool choices. That pattern usually holds for any language pick made under uncertainty: choose for principles you can defend a decade out, not for popularity you can measure today.

Frequently Asked Questions

What language is Quire built with?

Dart, on both client and server. The application is around 54,000 lines of Dart code with about 30 library imports.

Why did Quire choose Dart instead of JavaScript or TypeScript?

Strong typing, a class-based inheritance model, and one language across client and server. Dart also offered a few modern features ahead of broad JavaScript support.

Why didn't Quire use AngularDART or Polymer.dart?

The team wanted finer DOM control than AngularDART's binding allowed, and Quire's architectural patterns did not match Angular's logic. Polymer.dart was avoided because ShadowDOM conflicted with Bootstrap-style selectors at the time. Polymer was later deprecated, which validated the decision.

What are DQuery and Bootjack?

Open-source libraries from the Rikulo team (Quire's engineering team). DQuery is a partial port of jQuery to Dart focused on event delegation. Bootjack is a complete port of Bootstrap 3 with nearly identical CSS and APIs.

What is Stream in the Quire stack?

Quire's web server, written in pure Dart, using the event loop model. Routing, filters, and server-side MVC are built in. Quire runs Stream behind nginx, which handles scaling and HTTPS.

Simon Pai
Co-Founder at Bookshow