The mv-storage attribute tells Mavo where to store data, and mv-source where to read it from, if different.

Disabling storage

To disable storage, just don't include an mv-storage attribute, or specify mv-storage="none". You could also disable storage on a per-property basis, by specifying mv-storage="none" on individual properties. This is useful when you don't need a property's value to be saved. Properties whose values are specified via expressions have mv-storage="none" by default.

Local storage

You can save your data in the browser’s localStorage, by using the keyword local as the value of the mv-storage attribute (mv-storage="local").

This can be useful if you are not planning to share your data with other people. Please note that most localStorage implementations have a limit of 4MB. Also, localStorage is bound by the same-origin policy. This means that if you change your URL, your data will be left behind!


Mavo takes care of authentication when using Github as your storage backend.

The Github backend provides remote storage, which is useful if you need to share data with other people and a history of all edits, which is useful for recovering past data if you regret your changes at any point. Mavo will provide login controls and will not allow any edits until the viewer has logged in. If the file and/or repo does not exist, Mavo will create it upon saving.

If the logged in user does have commit permission (i.e. is either the creator of the repo or added as a collaborator), saving will directly save back to Github. If the logged in user does not have commit permissions, the data will be stored in their own profile and they will be prompted to send an "edit suggestion" to the original owner of the data. Editing suggestions are sent as pull requests, with a link to view the new data in the Mavo app (by using the storage URL parameter described below). Currently reviewing the changes between the new and old data (i.e. the diff) requires the ability to read JSON (unless you’re using a different format for your data). Future Mavo versions may introduce a graphical way for this as well.

You don’t need to understand how Github works to use it for storing your data. If you don’t know what a repo or a branch is, do not worry. Just sign up for a Github account, and specify as the value of the mv-storage attribute. Mavo will automatically create an mv-data repo for you and a file to store your data named APP_ID.json where APP_ID is the name of your app.

If you do understand how Github works, you can specify where your data should be stored with more precision:
Mavo will create a file called APP_ID.json in that repository.
Mavo will assume a default branch of master
The URL Github gives you when using its file browser. As specific as it gets, Mavo makes no assumptions.
The URL that shows when you click the button Raw from the Github file browser.

Terms in UPPERCASE_AND_ITALIC indicate that you are expected to replace them with the actual values (e.g. your username). Note that FILE_NAME above could include a path, e.g. foo/bar/baz.json.

Github Pages URLs ( are not supported, because it’s impossible for Mavo to figure out where to save the data just from the Github Pages URL.


The Dropbox backend also provides remote storage, and may be useful for people without a Github account. Just like the Github backend, it takes care of authentication, and only provides editing and saving controls to users that have appropriate permissions.

Unlike with Github, Mavo cannot create the file for you if it does not exist. To start using it, you need to create an empty file with a .json extension, and add it to your Dropbox. Then, in the Dropbox application, click "Share" and copy the link it gives you (you may need to click "Copy Link" to get to it). It will look like This link is what you will use in the mv-storage attribute:

HTML element

To store data in another HTML element in the page, use an mv-storage value of #[elementid] where [elementid] is the id of the element you want to use to store data in.

Note that unless you take extra steps to persist the content of that element, the data will be lost after a page refresh. This storage adapter is mainly useful for debugging purposes or to combine Mavo with other libraries.

Reading from a different data source

Sometimes you want to load data from one place and save to a different place, for a variety of reasons. If this is what you want, Mavo has you covered! Just use the mv-source attribute to specify where the data will be read from and the mv-storage attribute to specify where the data will be stored. Note that if you use both mv-source and mv-storage, only login controls for the latter will be shown in the Mavo bar.

Providing initial data

Sometimes you want to store your application’s data in the local store or another initially empty storage, but you want the app to start with some data already in place, if its storage is empty. For this purpose, you can use the mv-init attribute. It will be used just like the mv-source attribute above, but only if no data has been stored yet.

Changing mv-storage, mv-source, and mv-init via the URL

You can change the storage location of any Mavo app via the URL. This way, you can reuse the same Mavo app to edit multiple sets of data with the same general format or even reuse someone else’s Mavo app to store your own data in your own preferred location! If there is only one Mavo app on the page (or if the one you are interested in is first), you use the storage, source, and init URL parameters. For example, here is the To-Do list from the demos with a local data store.

While the storage, source, and init URL parameters only work for the first Mavo app on the page, you can use APP_ID-storage, APP_ID-source, and APP_ID-init for any Mavo app, regardless of its placement on the page. For example, here is the local To-Do list again, using URL parameters that include the app name.

Creating your own storage backend

To add support for a new backend, all you need is call Mavo.Backend.register() with a class that extends from Mavo.Backend. For information on how the $.Class() class helper works, look in Bliss’ documentation.

			// Mandatory. You may instead extend another backend, e.g. Mavo.Backend.Github
			extends: Mavo.Backend,

			id: "MyAwesomeBackend", // an id for your backend

			constructor: function(url, {mavo, format}) {
				// Initialization code

				// Already defined by the parent constructor:
				this.source // Raw URL (attribute value)
				this.url // URL object from this.source
				this.mavo // Mavo instance
				this.format // Current format
				this.permissions // Permissions of this particular backend.

			// Low-level functions for reading data. You don’t need to implement this
			// if the mv-storage/mv-source value is a URL and reading the data is just
			// a GET request to that URL.
			get: function(url) {
				// Should return a promise that resolves to the data as a string or object

			// High level function for reading data. Calls this.get().
			// You rarely need to override this.
			load: function() {
				// Should return a promise that resolves to the data as an object

			// Low-level saving code.
			// serialized: Data serialized according to this.format
			// path: Path to store data
			// o: Arbitrary options
			put: function(serialized, path = this.path, o = {}) {
				// Returns promise

			// If your backend supports uploads, this is mandatory.
			// file: File object to be uploaded
			// path: relative path to store uploads (e.g. "images")
			upload: function(file, path) {
				// Upload code. Should call this.put()

			// High level function for storing data.
			// You rarely need to override this, except to avoid serialization.
			store: function(data, {path, format = this.format} = {}) {
				// Should return a promise that resolves when the data is saved successfully

			// Takes care of authentication. If passive is true, only checks if
			// the user is already logged in, but does not present any login UI.
			// Typically, you’d call this.login(true) in the constructor
			login: function(passive) {
				// Typically, you’d check if a user is already authenticated
				// and return Promise.resolve() if so.

				// Returns promise that resolves when the user has successfully authenticated

			// Log current user out
			logout: function() {
				// Returns promise

			static: {
				// Mandatory and very important! This determines when your backend is used.
				// value: The mv-storage/mv-source/mv-init value
				test: function(value) {
					// Returns true if this value applies to this backend

Make sure to set this.permissions accordingly throughout. You can read more about Mavo.Permissions in the API documentation.

In case you’re writing a backend for a JSON-based OAuth 2 service, Mavo.Backend includes many helpers to make this as easy as possible. Take a look at the following in the API documentation:

Another helpful function you may write if your backend supports authentication, is the getUser function, which should make the API call to get user information and set this.user. You must set this.user before setting the logout permission to show user information in the Mavo toolbar.

	getUser: function() {
		if (this.user) {
			return Promise.resolve(this.user);

		return this.request("user").then(info => {
			this.user = { // replace the following fields with how you get username, name, profile picture, etc
				username: info.getUsernameHere,
				name: info.getNameHere,
				avatar: info.getProfilePictureHere,
				info // raw info object

			$.fire(this.mavo.element, "mavo:login", { backend: this });

You can see examples of backend declarations in src/backend.github.js, src/backend.dropbox.js and src/backend.js

If your backend may be useful to other people, please consider adding it to the Plugin directory!