How to build a simple notes app with IDX

Introduction

IDX is a protocol for building applications with user-centric, interoperable data. In a previous post, we described what IDX is and the benefits of building applications in this way. This tutorial will walk through the creation of a simple note-taking web app using decentralized technologies for authentication and data storage, allowing users to have complete ownership over their contents.

In this tutorial, we will use the following technologies:

The finished product ✨

Environment setup

To get started using IDX, we'll first need to install the IDX CLI using npm. You'll also need to have node installed.


Project setup

Initial dependencies

We'll use Create React App with the TypeScript template to setup our project:


Then we'll add the first dependencies:


Let's also edit the package.json file to add the following scripts:


Finally, as the uint8arrays package does not provide TypeScript definitions, let's add them to the src/react-app-env.d.ts file:


Local Ceramic node

Let's start a local Ceramic node using the script defined, and keep it running for all the steps of this tutorial:


We'll also need to bootstrap the local node with the IDX documents, using the IDX CLI previously installed:


Data model

By using Ceramic and IDX, we can create a data model for our application that is user-centric (instead of application-siloed),  globally available from any client on the Ceramic network, and publicly discoverable and shareable across any application(s) that a user interacts with. This is all made possible thanks to public schemas, data definitions, and the user's IDX document. Thus, by storing your application's user data in the IDX framework, it becomes truly user-centric and portable across app domains and contexts.

For this notes management app, we'll use two types of documents and therefore two schemas:

We will need to create and publish these schemas to the local Ceramic node, along with a definition which allows us to interact with the notes list using IDX. This will allow our app and others to access the notes via the following path:

User DID —> IDX index —> notes definition —> notes list —> note 1
                                                        -> note 2
                                                        -> ...

Let's create a bootstrap.js script that will create and publish the schemas and definition to our Ceramic node as TileDocuments, and store the Ceramic stream ID (StreamID) of the definition to a JSON file that will be used by the app:


Interacting with our data model

Before moving forwards with implementing our app, let's first check we can interact with our data model using the IDX CLI.

First, we'll need to create a local DID that will be used by the CLI:


The local alias can be used to reference the DID in the following commands, rather than having to provide the full DID string.

Now we can create a note with some text using the Note schema URL added to src/config.json. In my case this URL is ceramic://kjzl6cwe1jw14atxo8ax0mrknm7xfh8pxqy24hbdrxi9nagtwoa3la5s4hf32qr but it will be different when using another seed, so don't forget to change it in the example below:


Successfully running this command will display the stream ID of the created note, that we can then add to the list of notes using the notes definition key added to src/config.json:


Make sure to replace the definition key (kjzl6cwe1jw14almqh93rmt1mpv8h6zvyeyhh7jzlqp95kgv38c0jb5yuo8mo7w in my case) and the created note URL (ceramic://kjzl6cwe1jw147xhyjemd6r38812gzhhexw9mj3gz2vvkimotu5xktssik2cbzp in the code above) with the values created in your environment.

We can now check the created note and notes list can be loaded:


Application dependencies and IDX setup

Our app is going to use the following additional dependencies:


Then, let's create an idx.ts file in the src folder, implementing the logic to authenticate using a provided seed and load notes with IDX:


Application state and actions

In this section, we'll implement the core logic of our app based on a state, synchronous actions mutating the state, and high-level handlers performing one or more actions.

First, let's create a state.ts file in the src folder with the following initial contents:


Here we are importing types and dependencies from React (the useCallback and useReducer hooks) and defining the valid shapes the application State can have.

Next we'll define an Action type that includes all the possible synchronous actions that can be performed to mutate the State:


To handle the state transitions, we'll use a reducer function, as presented in React's documentation:


Finally, let's create a React hook wrapping this logic in high-level handlers:


Application UI

Now that our application logic is implemented, we can add the user interface, based on Material UI components.

For simplicity in this tutorial all the components are implemented in a single file, but more complex apps would gain from having the interface split into different modules.

Let's change the generated App.tsx file in the src folder, first to import the dependencies we'll use:


Next, let's create the styles we will use:


Now we'll create our first component: NotesList. This component displays the list of notes in a side menu, and a button to open a draft note.

This component will get the State and needed actions injected:


Another component we will need is AuthenticateScreen, that will display the authenticated ID or seed prompt as needed:


Next, let's add the DraftScreen, allowing users to create and save a new note:


We will use another component, NoteScreen, for displaying an existing note as the logic is a bit different from a draft, notably that we have to first load the document from Ceramic before being able to display the note contents:


Finally, we can use all these components in the top-level App component exported by this module:


That's it!

You should now be able to start the app using the npm start command that will compile and start a local server.

The application logic and UI in this tutorial is intentionally kept simple for demonstration purposes, but could be greatly improved for a real app.

The full code for this tutorial is available on GitHub if you want to use it as a basis for your own experiments.

Moving to production

Using a local Ceramic node is an easy way to get started developing an app, but what about production use cases?

We are working with infrastructure partners who are eager to provide production-grade Ceramic node hosting services that we will recommend once ready. Other options are for you to host a Ceramic node for your users, or alternatively, build your app using a full Ceramic node in-browser with @ceramicnetwork/core (instead of the @ceramicnetwork/http-client used in this tutorial). Future tutorials and guides will dive into how to set up production Ceramic deployments.

In the meantime, feel free to reach out on the Ceramic Discord if you need support deploying Ceramic.

What's next?

Learn more about IDX by visiting the IDX website and documentation.

If you need any help or just want to say hi, join the IDX Discord channel!