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 appearancedark: Dark themegridstack: Dashboard-style layoutreveal: 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:
Push notebook to GitHub
Go to mybinder.org
Enter GitHub URL
Add
?urlpath=voilato 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.