diff --git a/README.md b/README.md
index a3e5841..e80eb10 100644
--- a/README.md
+++ b/README.md
@@ -1,132 +1,14 @@
# flask-sock
[![Build status](https://github.com/miguelgrinberg/flask-sock/workflows/build/badge.svg)](https://github.com/miguelgrinberg/flask-sock/actions) [![codecov](https://codecov.io/gh/miguelgrinberg/flask-sock/branch/main/graph/badge.svg)](https://codecov.io/gh/miguelgrinberg/flask-sock)
-WebSocket support for Flask (and possibly other WSGI frameworks). What makes
-this package different than other WebSocket servers is that it does not require
-a greenlet based server (gevent, eventlet) to work.
+WebSocket support for Flask. What makes this extension different than others
+is that it does not require a greenlet based server (gevent, eventlet) to work.
-This WebSocket implementation is compatible with the following synchronous
-web servers:
+This WebSocket implementation is compatible with the Flask development web
+server. For a production deployment it can be used with Gunicorn, Eventlet or
+Gevent.
-- Werkzeug (Flask's development web server)
-- Gunicorn with the `gthread` worker
+## Resources
-In addition to the servers above, the following asynchronous web servers are
-supported when the Python standard library is monkey patched:
-
-- Eventlet's WSGI server
-- Gevent's WSGI server
-- Gunicorn with the `eventlet` worker
-- Gunicorn with the `gevent` worker
-
-## Installation
-
-```bash
-pip install flask-sock
-```
-
-## Example
-
-```python
-from flask import Flask, render_template
-from flask_sock import Sock
-
-app = Flask(__name__)
-sock = Sock(app)
-
-
-@sock.route('/echo')
-def echo(ws):
- while True:
- data = ws.receive()
- ws.send(data)
-```
-
-## Running
-
-To run an application that uses this package, you need to use a supported web
-server.
-
-### Running with Werkzeug
-
-Werkzeug supports WebSocket routing in version 2, which at this time hasn't
-been officially released. You can install a supported release candidate with
-the following command:
-
-```bash
-pip install "werkzeug>=2.0.0rc3"
-```
-
-To run your application use the normal method that you always use. Both the
-`flask run` and `app.run()` methods of starting the Flask application should
-work.
-
-### Running with Gunicorn
-
-To use this package with Gunicorn you need to keep in mind that each active
-WebSocket client will use up a worker. The most practical way to run a
-WebSocket server is to use the `--threads` option to allocate the number of
-clients that you need:
-
-```bash
-gunicorn -b :5000 --threads 100 module:app
-```
-
-It is also okay to use multiple workers, each with a number of allocated
-threads:
-
-```bash
-gunicorn -b :5000 --workers 4 --threads 100 module:app
-```
-
-### Running with eventlet
-
-To serve your application with the eventlet WSGI server you can use the
-following script:
-
-```python
-import eventlet
-eventlet.monkey_patch()
-
-from eventlet import wsgi
-from module import app
-
-wsgi.server(eventlet.listen(('', 5000)), app)
-```
-
-It is also possible to use Gunicorn's `eventlet` worker:
-
-```bash
-gunicorn -b :5000 --worker-class eventlet module:app
-```
-
-Gunicorn's `eventlet` worker handles a maximum of 1000 concurrent requests in a
-single worker process by default. The maximum number of concurrent requests
-can be changed with the `--worker-connections` option. The number of workers
-can be changed with the `--workers` option.
-
-### Running with gevent
-
-To serve your application with the gevent WSGI server you can use the following
-script:
-
-```python
-from gevent import monkey
-monkey.patch_all()
-
-from gevent.pywsgi import WSGIServer
-from module import app
-
-WSGIServer(('127.0.0.1', 5000), app).serve_forever()
-```
-
-It is also possible to use Gunicorn's `gevent` worker:
-
-```bash
-gunicorn -b :5000 --worker-class gevent module:app
-```
-
-Gunicorn's `gevent` worker handles a maximum of 1000 concurrent requests in a
-single worker process by default. The maximum number of concurrent requests
-can be changed with the `--worker-connections` option. The number of workers
-can be changed with the `--workers` option.
+- [Documentation](http://flask-sock.readthedocs.io/en/latest/)
+- [PyPI](https://pypi.python.org/pypi/flask-sock)
diff --git a/docs/Makefile b/docs/Makefile
new file mode 100644
index 0000000..d4bb2cb
--- /dev/null
+++ b/docs/Makefile
@@ -0,0 +1,20 @@
+# Minimal makefile for Sphinx documentation
+#
+
+# You can set these variables from the command line, and also
+# from the environment for the first two.
+SPHINXOPTS ?=
+SPHINXBUILD ?= sphinx-build
+SOURCEDIR = .
+BUILDDIR = _build
+
+# Put it first so that "make" without argument is like "make help".
+help:
+ @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
+
+.PHONY: help Makefile
+
+# Catch-all target: route all unknown targets to Sphinx using the new
+# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS).
+%: Makefile
+ @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
diff --git a/docs/conf.py b/docs/conf.py
new file mode 100644
index 0000000..4a04dca
--- /dev/null
+++ b/docs/conf.py
@@ -0,0 +1,61 @@
+# Configuration file for the Sphinx documentation builder.
+#
+# This file only contains a selection of the most common options. For a full
+# list see the documentation:
+# https://www.sphinx-doc.org/en/master/usage/configuration.html
+
+# -- Path setup --------------------------------------------------------------
+
+# If extensions (or modules to document with autodoc) are in another directory,
+# add these directories to sys.path here. If the directory is relative to the
+# documentation root, use os.path.abspath to make it absolute, like shown here.
+#
+# import os
+# import sys
+# sys.path.insert(0, os.path.abspath('.'))
+
+
+# -- Project information -----------------------------------------------------
+
+project = 'Flask-Sock'
+copyright = '2021, Miguel Grinberg'
+author = 'Miguel Grinberg'
+
+
+# -- General configuration ---------------------------------------------------
+
+# Add any Sphinx extension module names here, as strings. They can be
+# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
+# ones.
+extensions = [
+]
+
+# Add any paths that contain templates here, relative to this directory.
+templates_path = ['_templates']
+
+# List of patterns, relative to source directory, that match files and
+# directories to ignore when looking for source files.
+# This pattern also affects html_static_path and html_extra_path.
+exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store']
+
+
+# -- Options for HTML output -------------------------------------------------
+
+# The theme to use for HTML and HTML Help pages. See the documentation for
+# a list of builtin themes.
+#
+html_theme = 'alabaster'
+
+# Add any paths that contain custom static files (such as style sheets) here,
+# relative to this directory. They are copied after the builtin static files,
+# so a file named "default.css" will overwrite the builtin "default.css".
+html_static_path = ['_static']
+
+html_theme_options = {
+ 'github_user': 'miguelgrinberg',
+ 'github_repo': 'flask-sock',
+ 'github_banner': True,
+ 'github_button': True,
+ 'github_type': 'star',
+ 'fixed_sidebar': True,
+}
diff --git a/docs/index.rst b/docs/index.rst
new file mode 100644
index 0000000..a75e829
--- /dev/null
+++ b/docs/index.rst
@@ -0,0 +1,22 @@
+.. Flask-Sock documentation master file, created by
+ sphinx-quickstart on Thu Jun 3 16:15:31 2021.
+ You can adapt this file completely to your liking, but it should at least
+ contain the root `toctree` directive.
+
+Flask-Sock
+==========
+
+WebSocket support for Flask. What makes this extension different than others
+is that it works with the Flask development web server, without the need to
+install a greenlet based server such as gevent or eventlet.
+
+.. toctree::
+ :maxdepth: 2
+
+ quickstart
+ web_servers
+
+
+* :ref:`genindex`
+* :ref:`modindex`
+* :ref:`search`
diff --git a/docs/make.bat b/docs/make.bat
new file mode 100644
index 0000000..2119f51
--- /dev/null
+++ b/docs/make.bat
@@ -0,0 +1,35 @@
+@ECHO OFF
+
+pushd %~dp0
+
+REM Command file for Sphinx documentation
+
+if "%SPHINXBUILD%" == "" (
+ set SPHINXBUILD=sphinx-build
+)
+set SOURCEDIR=.
+set BUILDDIR=_build
+
+if "%1" == "" goto help
+
+%SPHINXBUILD% >NUL 2>NUL
+if errorlevel 9009 (
+ echo.
+ echo.The 'sphinx-build' command was not found. Make sure you have Sphinx
+ echo.installed, then set the SPHINXBUILD environment variable to point
+ echo.to the full path of the 'sphinx-build' executable. Alternatively you
+ echo.may add the Sphinx directory to PATH.
+ echo.
+ echo.If you don't have Sphinx installed, grab it from
+ echo.http://sphinx-doc.org/
+ exit /b 1
+)
+
+%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O%
+goto end
+
+:help
+%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O%
+
+:end
+popd
diff --git a/docs/quickstart.rst b/docs/quickstart.rst
new file mode 100644
index 0000000..82b392e
--- /dev/null
+++ b/docs/quickstart.rst
@@ -0,0 +1,24 @@
+Installation
+------------
+
+::
+
+ pip install flask-sock
+
+Example
+-------
+
+::
+
+ from flask import Flask, render_template
+ from flask_sock import Sock
+
+ app = Flask(__name__)
+ sock = Sock(app)
+
+
+ @sock.route('/echo')
+ def echo(ws):
+ while True:
+ data = ws.receive()
+ ws.send(data)
diff --git a/docs/web_servers.rst b/docs/web_servers.rst
new file mode 100644
index 0000000..64d17e3
--- /dev/null
+++ b/docs/web_servers.rst
@@ -0,0 +1,82 @@
+Supported web servers
+---------------------
+
+This extensions can be used to create Flask routes that handle WebSocket
+requests when used with `Werkzeug `_,
+`Gunicorn `_, `Eventlet `_ or
+`Gevent `_. The following sections describe deployment
+details specific to each of these web servers.
+
+Werkzeug (Flask development web server)
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Werkzeug supports WebSocket routing in version 2, which means that this
+extension can only be used with versions of Flask starting at 2.0.
+
+To run your application use the normal method that you always use. Both the
+``flask run`` and ``app.run()`` methods of starting the Flask application
+should work.
+
+Gunicorn
+~~~~~~~~
+
+To use this package with Gunicorn you need to keep in mind that each active
+WebSocket client will use up a worker. For that reason, the most practical way
+to run a WebSocket server is to use the ``--threads`` option to allocate the
+number of clients that you need::
+
+ gunicorn -b :5000 --threads 100 module:app
+
+It is also okay to use multiple workers, each with a number of allocated
+threads::
+
+ gunicorn -b :5000 --workers 4 --threads 100 module:app
+
+Eventlet
+~~~~~~~~
+
+To serve your application with the eventlet WSGI server you can use the
+following script::
+
+ import eventlet
+ eventlet.monkey_patch()
+
+ from eventlet import wsgi
+ from module import app
+
+ wsgi.server(eventlet.listen(('', 5000)), app)
+
+It is also possible to use Gunicorn's eventlet worker::
+
+ gunicorn -b :5000 --worker-class eventlet module:app
+
+Gunicorn's eventlet worker handles a maximum of 1000 concurrent requests in a
+single worker process by default. The maximum number of concurrent requests can
+be changed with the ``--worker-connections`` option. The number of workers can
+be changed with the ``--workers`` option.
+
+Gevent
+~~~~~~
+
+To serve your application with the gevent WSGI server you can use the
+following script::
+
+ from gevent import monkey
+ monkey.patch_all()
+
+ from gevent.pywsgi import WSGIServer
+ from module import app
+
+ WSGIServer(('127.0.0.1', 5000), app).serve_forever()
+
+It is also possible to use Gunicorn's gevent worker::
+
+ gunicorn -b :5000 --worker-class gevent module:app
+
+Gunicorn's gevent worker handles a maximum of 1000 concurrent requests in a
+single worker process by default. The maximum number of concurrent requests can
+be changed with the ``--worker-connections`` option. The number of workers can
+be changed with the ``--workers`` option.
+
+Unless other WebSocket packages for Gevent, this extension does not require the
+``gevent-websocket`` package to be installed.