Introduction

The Quip Live Apps API enables developers to extend the Quip document canvas with interactive, custom components. The goal of the platform is to expand the scope and capabilities of Quip's living documents:

Enable rich, visual forms of communication within documents

Enable end users to transform documents into tools for custom and complex workflows

Enable Quip documents to deeply integrate with third party services

Why Build on Quip?

We've spent years building a product that's changing the face of productivity and collaboration. Now, we're turning Quip into a platform where developers can integrate solutions directly into the Quip canvas. Apps work cross-platform and inherit collaboration and social features as well as notification and offline capabilities.

Type @Hello World in the document (or @ followed by your app's name from app/manifest.json). You should see your app listed in the insert menu.

Hit ENTER to insert your app into the document.

You should now see your app (with the text "Hello, world!") rendered into the Quip document!

Develop Your App

You've deployed your first app! The next step is iterating on your app locally to make it better. To do that, you're going to run a local webserver and connect your document's app to it. Then, you can edit your code and just reload your app to pick up changes.

Run npm start in your terminal in your my-app directory.

Run this command whenever you work on your app, then leave it running continuously — it will automatically watch for code changes and update the compiled files as you edit.

If you have a syntax error in your code, it will show up here.This uses webpack to compile your .js/.jsx and .less files into a single .js file and .css file in my-app/app/dist.

In your Quip document, focus your app by clicking on it. Your app's "Hello World" menu button will appear. Click "Hello World → Use Local Resources" in the Developer section of that menu.This will save a setting so that your app will load the code from the local server you're running with npm start on https://localhost:8888/.

Now, reloading your app via "Hello World → Reload" will pick up any local changes you make without requiring you to upload a new app.ele to Quip!

Note if you make any changes to the manifest.json file, you will need to re-run npm run build and then upload your app.ele file to get those updates.

Now open my-app/src/App.jsx in your editor, find the text "Hello, world!", and change it to something else:

Building a Sticky Note App

Let's make your app into something more exciting — a sticky note app that anyone on the document can edit. You'll see firsthand some of the collaborative abilities of the Quip Live Apps API. We'll also demonstrate how to store real data in an app. First we'll set up the data model, then add a Quip rich text box, and finally tweak the CSS.

Note that the app is not ready until you reach the final step (don't try to reload until then).

Introducing Properties

All app data must live in a property of either the RootRecord or a descendant of the RootRecord. These properties can be primitives (number, string, boolean, etc), RecordList objects, or Record objects with properties of their own.

For this app, we want to set up a property for a special type of record designed to store collaborative text — a RichTextRecord. This property will be defined on the RootRecord.

To start adding properties to the root record, you must first define a custom class that extends quip.apps.RootRecord. Then, you must implement the getProperties() function to define the name and type of each property that can live on the record. The API reference explains more about records, properties, and data.

For now, update the code in root.jsx as follows to set up the property and pass it into <App/>:

Notice how we first create StickyNoteRoot to extend quip.apps.RootRecord. We then implement getProperties() to define the stickyNote property. We say that the stickyNote property has type RichTextRecord, which we need to store our collaborative text.

Here's a deep dive into the rest of this code. If you'd like, just skim it for now to get your sticky note working first.

Deep Dive: Registering the Record Class

You can see that this code calls quip.apps.registerClass after creating StickyNoteRoot. For every record class you define, you also must call quip.apps.registerClass to inform the Quip API about the record class. This Quip API method takes as parameters the record class and a unique string identifier. The string identifier must not change once set (otherwise existing instances of your app may break). The call must happen before quip.apps.initialize.

You can only ever register one class that extends quip.apps.RootRecord. Once you do so, all calls to quip.apps.getRootRecord() will return instances of your custom root class. Note that more complicated apps will want to define descendant records that also have properties — to do so, just extend quip.apps.Record in the same fashion.

Deep Dive: Creating the Property

Now we're ready to actually create and use the stickyNote property! We create the property by calling rootRecord.set("stickyNote", {}). The first parameter of set is the name of the property. set knows that it should create a new RichTextRecord record for stickyNote because of the definition in getProperties. The second parameter is an object that contains child properties to set on the newly-created RichTextRecord. In this case, we don't have any special properties for stickyNote so we pass an empty object.

Once you set a property, it lives on the record for as long as the app exists in the document. That's why we only call set for stickyNote when the app is first created. We detect creation by checking params.isCreation in quip.apps.initialize, where the params object is passed to our app from the Quip document. When params.isCreation is false, quip.apps.initialize is being called on an existing app. In that case, our set-up code already ran and the stickyNote property already lives on the root record. We shouldn't set it up again since it might now contain data.

Deep Dive: Getting the Property

The last thing we do is call rootRecord.get("stickyNote") to access the RichTextRecord object stored in the property. We pass the result into <App/> as a React prop, ready to use. (If the idea of React props is new to you, the React tutorial will help.)

Adding Rich Text

Now we can add the RichTextBox React component to make the app collaborative!

A RichTextBox is one of the powerful Quip platform building blocks. It seamlessly handles edits from different users and provides all the functionality of the Quip editor, including styling and collaborative features like @mentions.

Adding a RichTextBox is incredibly easy. All we need to do is pass it the RichTextRecord — it handles everything else.