Go back

Developing Quizle.io

Quizle.io is a side project of Codeurs. It allows you to easily create a quiz that can be shared with others.

Try it out

At Codeurs we have a show and tell moment every few months — everyone picks a topic and prepares a short presentation. This time around I wanted to do something a little more interactive and prepare a short quiz on the React API. After a quick Google search, I failed to find a tool to quickly create a quiz. Existing tools often require creating an account to test out their quiz editor.
Once signed in, creating quiz questions requires a lot of clicking around and completely pulls you out of the flow of coming up with questions and answers. 

I ended up writing the quiz in Markdown, rendering it to a PDF and collecting answers on post-it notes. The quiz scoring was confusing, the questions too difficult, and I ended up being the only one having fun. But that is besides the point of this blog post. What I was after was a tool where I could quickly create a quiz and write the questions and answers as straightforward as editing a Markdown document.

Building it yourself

The experiment started by thinking of the quiz as a single document, not a collection of questions that need separate editing. This would require an editor where the cursor can naturally move between questions and answers. Text selection is then all that is needed to move questions or answers around or delete them. Hitting the enter button should be enough to create a new question.

Creating a quiz editor

During development of alinea I've built up some experience with Tiptap — a JavaScript library which helps you create custom rich text editors. It uses ProseMirror under the hood but makes it accessible from frontend frameworks like React or Vue. 

Tiptap includes a number of useful plugins to get started, you will not spend all your time creating a plugin to make text bold. To create the quiz editor I started by creating custom Nodes. Nodes are sections within your document that you can give its own styling and DOM elements. For example a "slide" node that can hold its own title, body text and answers. The most complicated plugin is one that takes over the behavior of the enter key. It requires traversing the ProseMirror state of the document and inserting new slide, title or answer nodes based on the current position.

Making it collaborative

Tiptap includes a collaborative plugin based on Y.js. Y.js is an implementation of CRDTs in JavaScript. CRDTs (Conflict-free Replicated Data Types) are a clever way to let multiple people update the same information without conflicts, by following certain rules. Y.js bundles the power of CRDTs in types like maps or arrays that are shareable with other users. All users can edit at the same time and as changes are transported to the other users, everyone ends up with the same data. Y.js supports different providers to facilitate the transporting. This includes transporting over Websockets or WebRTC.

The WebRTC option is very interesting, it allows users to exchange data peer-to-peer, skipping a server altogether. It technically still requires a signaling server to facilitate connections, but the amount of work that server needs to do is very minimal. Minimal enough that I was able to host it on a free tier.

All data is also transported to a local IndexedDB database, so no data is lost. Creating a new quiz creates a URL with a unique ID. Sharing the URL is enough to have someone else collaborate on the same quiz.


After building an initial quiz editor, I sketched out the next part. In the context that I saw us using the tool we're already presenting to a big screen. During a quiz session, we'll have users answering a question on their phone while they can also consult the questions and general progress on the big screen. The interface is responsive and mirrored on the phones as well, so having a shared screen is technically not even required. A quiz session is started from the editor. We select random digits in the form of 999-999 as a quiz pin code. This limits the amount of sessions that can be opened at the same time to a million, but that can be tweaked if we see it gain immense popularity.

Collecting answers

A quiz session state is represented in a single Y.js document. Users edit the document to add their answers, which is then synced with everyone else. While Y.js has a presence concept which can be used to sync metadata such as their username or online status I chose not to use this as it is fleeting in nature. If someone signs out by mistake (they close or refresh their tab) we still want to hold on to the user data, so we can list the user in the ranking at the end. It would be perfect to signal online presence, but for now there's no indication in the UI for it.

Hosting it

The frontend was built in TypeScript, using Vite.js to bundle the codebase into a few static assets. These are hosted on Cloudflare Pages where hosting static pages is completely free without bandwidth restrictions.

Give quizle.io a try