diff --git a/docs/source/comms.rst b/docs/source/comms.rst
new file mode 100644
index 000000000..cc03a8baa
--- /dev/null
+++ b/docs/source/comms.rst
@@ -0,0 +1,93 @@
+Comms
+=====
+
+*Comms* allow custom messages between the frontend and the kernel. They are used,
+for instance, in `ipywidgets `__ to
+update widget state.
+
+A comm consists of a pair of objects, in the kernel and the frontend, with an
+automatically assigned unique ID. When one side sends a message, a callback on
+the other side is triggered with that message data. Either side can open or
+close the comm.
+
+.. seealso::
+
+ `Custom Messages `__
+ The messaging specification section on comms
+
+Opening a comm from the kernel
+------------------------------
+
+First, the function to accept the comm must be available on the frontend. This
+can either be specified in a requirejs module, or registered in a registry, for
+example when an :doc:`extension ` is loaded. The
+latter is what we'll show here:
+
+.. code-block:: javascript
+
+ Jupyter.notebook.kernel.comm_manager.register_target('my_comm_target',
+ function(comm, msg) {
+ // comm is the frontend comm instance
+ // msg is the comm_open message, which can carry data
+
+ // Register handlers for later messages:
+ comm.on_msg(function(msg) {...});
+ comm.on_close(function(msg) {...});
+ comm.send({'foo': 0});
+ });
+
+Now you can open it from the kernel::
+
+ from ipykernel.comm import Comm
+ my_comm = Comm(target_name='my_comm_target', data={'foo': 1})
+ my_comm.send({'foo': 2})
+
+ # Add a callback for received messages.
+ @my_comm.on_msg
+ def _recv(msg):
+ # Use msg['content']['data'] for the data in the message
+
+
+This example is for IPython; its up to each kernel what API, if any, it offers
+for using comms.
+
+Opening a comm from the frontend
+--------------------------------
+
+This is very similar to above, but in reverse. First, a comm target must be
+registered in the kernel. For instance, this may be done by code displaying
+output: it will register a target in the kernel, and then display output
+containing Javascript to connect to it.
+
+::
+
+ def target_func(comm, msg):
+ # comm is the kernel Comm instance
+ # msg is the comm_open message
+
+ # Register handler for later messages
+ @comm.on_msg
+ def _recv(msg):
+ # Use msg['content']['data'] for the data in the message
+
+ # Send data to the frontend
+ comm.send({'foo': 5})
+
+ get_ipython().kernel.comm_manager.register_target('my_comm_target', target_func)
+
+This example is for IPython again; this will be different in other kernels that
+support comms.
+
+And then open it from the frontend:
+
+.. code-block:: javascript
+
+ comm = Jupyter.notebook.kernel.comm_manager.new_comm('my_comm_target',
+ {'foo': 6})
+ // Send data
+ comm.send({'foo': 7})
+
+ // Register a handler
+ comm.on_msg(function(msg) {
+ console.log(msg.content.data.foo);
+ });
diff --git a/docs/source/index.rst b/docs/source/index.rst
index ae1cbd6c0..f44bf8514 100644
--- a/docs/source/index.rst
+++ b/docs/source/index.rst
@@ -11,6 +11,7 @@ The Jupyter notebook
Running the Notebook
Migrating from IPython
ui_components
+ comms
.. toctree::
:maxdepth: 2