9.1. Notebook Applications with Voila#

Voila transforms Jupyter notebooks into standalone web applications. It’s the quickest path from an interactive notebook to a deployable application, requiring minimal code changes.

9.1.1. What is Voila?#

Voila:

  • Renders Jupyter notebooks as web apps

  • Hides code cells, shows only outputs and widgets

  • Executes notebook on the server

  • Provides live, interactive widgets

Advantage: If you have a notebook with ipywidgets, you’re 90% done!

9.1.2. Installation#

pip install voila

9.1.3. Your First Voila App#

9.1.3.1. Step 1: Create a Notebook#

Create my_app.ipynb:

# Cell 1: Imports
import ipywidgets as widgets
import matplotlib.pyplot as plt
import numpy as np

# Cell 2: Interactive plot
@widgets.interact(frequency=(0.1, 5, 0.1), amplitude=(0.1, 2, 0.1))
def plot_sine(frequency=1.0, amplitude=1.0):
    x = np.linspace(0, 4*np.pi, 500)
    y = amplitude * np.sin(frequency * x)
    
    plt.figure(figsize=(10, 4))
    plt.plot(x, y)
    plt.title(f'Sine Wave: amplitude={amplitude}, frequency={frequency}')
    plt.ylim(-2.5, 2.5)
    plt.show()

9.1.3.2. Step 2: Run with Voila#

voila my_app.ipynb

Voila serves the notebook at http://localhost:8866

Result: Interactive app with sliders, no code visible!

9.1.4. Voila Features#

9.1.4.1. Hide Code Cells#

Code is automatically hidden. Only outputs and widgets visible.

9.1.4.2. Custom Templates#

Change the appearance:

voila my_app.ipynb --template=dark

Available templates:

  • default: Standard Jupyter appearance

  • dark: Dark theme

  • gridstack: Dashboard-style layout

  • reveal: Presentation mode

9.1.4.3. Multiple Notebooks as Dashboard#

Serve a directory of notebooks:

voila notebooks/

Users see a list of available apps.

9.1.5. ML Model Example with Voila#

prediction_app.ipynb:

# Cell 1
import joblib
import ipywidgets as widgets
import pandas as pd

model = joblib.load('models/churn_model.joblib')

# Cell 2  
age_slider = widgets.IntSlider(min=18, max=80, value=35, description='Age:')
income_slider = widgets.IntSlider(min=20000, max=200000, value=50000, step=5000, description='Income:')
credit_slider = widgets.IntSlider(min=300, max=850, value=650, description='Credit Score:')
predict_button = widgets.Button(description='Predict Churn', button_style='primary')
output = widgets.Output()

def on_predict(b):
    with output:
        output.clear_output()
        features = [[age_slider.value, income_slider.value, credit_slider.value]]
        prediction = model.predict(features)[0]
        probability = model.predict_proba(features)[0][1]
        
        print(f"{'='*40}")
        print(f"PREDICTION: {'CHURN' if prediction == 1 else 'RETAIN'}")
        print(f"Churn Probability: {probability:.1%}")
        print(f"{'='*40}")

predict_button.on_click(on_predict)

# Cell 3
display(widgets.VBox([
    age_slider,
    income_slider,
    credit_slider,
    predict_button,
    output
]))
---------------------------------------------------------------------------
FileNotFoundError                         Traceback (most recent call last)
Cell In[2], line 6
      3 import ipywidgets as widgets
      4 import pandas as pd
----> 6 model = joblib.load('models/churn_model.joblib')
      8 # Cell 2  
      9 age_slider = widgets.IntSlider(min=18, max=80, value=35, description='Age:')

File ~/work/datasciencethenovel/datasciencethenovel/.venv/lib/python3.13/site-packages/joblib/numpy_pickle.py:735, in load(filename, mmap_mode, ensure_native_byte_order)
    733         obj = _unpickle(fobj, ensure_native_byte_order=ensure_native_byte_order)
    734 else:
--> 735     with open(filename, "rb") as f:
    736         with _validate_fileobject_and_memmap(f, filename, mmap_mode) as (
    737             fobj,
    738             validated_mmap_mode,
    739         ):
    740             if isinstance(fobj, str):
    741                 # if the returned file object is a string, this means we
    742                 # try to load a pickle file generated with an version of
    743                 # Joblib so we load it with joblib compatibility function.

FileNotFoundError: [Errno 2] No such file or directory: 'models/churn_model.joblib'

Deploy:

voila prediction_app.ipynb

9.1.6. Voila Configuration#

Create voila.json:

{
  "VoilaConfiguration": {
    "template": "dark",
    "theme": "dark",
    "show_tracebacks": false,
    "enable_nbextensions": true
  }
}

9.1.7. Deployment Options#

9.1.7.1. Local Network#

voila my_app.ipynb --port=8866 --no-browser

Access from other computers on your network.

9.1.7.2. Voila on Binder#

Deploy to cloud for free:

  1. Push notebook to GitHub

  2. Go to mybinder.org

  3. Enter GitHub URL

  4. Add ?urlpath=voila to Binder URL

9.1.7.3. Voila on Cloud Platforms#

Deploy to Heroku, AWS, GCP, etc.

Dockerfile:

FROM jupyter/minimal-notebook

RUN pip install voila

COPY my_app.ipynb /home/jovyan/
COPY models/ /home/jovyan/models/

CMD [\"voila\", \"my_app.ipynb\", \"--port=8866\", \"--no-browser\"]

9.1.8. Pros and Cons#

9.1.8.1. Advantages#

  • Minimal effort: Existing notebooks work

  • No new framework: Use familiar Jupyter/widgets

  • Quick prototypes: Notebook to app in minutes

  • Good for demos: Share analysis quickly

9.1.8.2. Limitations#

  • Limited customization: Notebook-based UI

  • Performance: Full notebook execution

  • Scaling: Not ideal for many users

  • Layout constraints: Notebook cell structure

9.1.9. When to Use Voila#

Use Voila when:

  • You have a working interactive notebook

  • Need to share quickly with stakeholders

  • Audience is small (internal team)

  • Prototype/proof-of-concept stage

Skip Voila when:

  • Need custom, polished UI

  • High traffic expected

  • Complex application logic

  • Need fine-grained control

9.1.10. Best Practices#

9.1.10.1. DO:#

  • Keep notebooks focused and simple

  • Test thoroughly before deploying

  • Use clear widget labels and descriptions

  • Handle errors gracefully

  • Add markdown cells for context

9.1.10.2. DON’T:#

  • Include long-running cells at startup

  • Display unnecessary outputs

  • Use print statements (use Output widgets)

  • Rely on cell execution order

9.1.11. Summary#

Voila provides the fastest path from notebook to web app:

  • Minimal code changes required

  • Uses existing ipywidgets

  • Good for prototypes and internal tools

  • Limited customization

Next: Streamlit for more flexible, custom applications.