Bring your ideas to life with hassle-free web and Android app development.
Hi everyone, In this post I will show how you can create a single page application with flask. People generally think that flask is not capable of building advance applications and certainly not single page ones, but that’s entirely wrong. There is a library named Flask-HTMX which is really easy to use and today we’re going to build a single page web application using flask with the help of this library. To follow along the tutorial, you need to know basic Python and its Flask framework.
First of all, let’s take a look at what we’re going to build in this tutorial. And then we’ll go through the step by step guide.
Final Result
As you can clearly see, the page doesn’t reload and the content gets changed dynamically on pressing the specific button.
Prerequisites before we start developing the Single Page Application
Python3 needs to be installed in your computer.
Flask and Flask-HTMX needs to be installed. You can install them by using the command down below.
pip install flask flask-htmx
After installing these packages, you’re ready to begin coding.
Want us to create your website?
Guess what? You’re in luck because we are a professional web and app development agency.
Making the Templates for our Single Page Application
Create a HTML file named base.html inside the templates directory. This will contain the header and footer code which will be constant in all the pages. We’re also going to use bootstrap 5 in this tutorial to make the design look good without writing much CSS.
base.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<!-- BOOTSTRAP CSS CDN -->
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet"
integrity="sha384-QWTKZyjpPEjISv5WaRU9OFeRpok6YctnYmDr5pNlyT2bRjXh0JMhjY6hW+ALEwIH" crossorigin="anonymous">
<title>Single Page Flask App - Vixion</title>
</head>
<body>
<!-- Navbar -->
<nav class="navbar navbar-expand-lg navbar-light bg-light shadow-sm">
<div class="container">
<a class="navbar-brand fw-bold" href="#">Vixion</a>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarNav">
<ul class="navbar-nav ms-auto">
<li class="nav-item"><a class="nav-link" href="" hx-get="/" hx-on="click" hx-target="#main-container" hx-swap="innerHTML">Services</a></li>
<li class="nav-item"><a class="nav-link" href="" hx-get="/about" hx-on="click" hx-target="#main-container" hx-swap="innerHTML">About</a></li>
<li class="nav-item"><a class="nav-link" href="" hx-get="/contact" hx-on="click" hx-target="#main-container" hx-swap="innerHTML">Contact</a></li>
</ul>
</div>
</div>
</nav>
<div class="container" id="main-container">
{% include page %}
</div>
<!-- Footer -->
<footer class="bg-dark text-white text-center py-3">
<p class="mb-0">© 2024 Vixion. All rights reserved.</p>
</footer>
<!-- HTMX CDN -->
<script src="https://unpkg.com/htmx.org@2.0.3" integrity="sha384-0895/pl2MU10Hqc6jd4RvrthNlDiE9U1tWmX7WRESftEDRosgxNsQG/Ze9YMRzHq" crossorigin="anonymous"></script>
<!-- Bootstrap JS CDN -->
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"
integrity="sha384-YvpcrYf0tY3lHB60NNkmXc5s9fDVZLESaAA55NDzOxhy9GkcIdslK1eN7N6jIeHz"
crossorigin="anonymous"></script>
</body>
</html>
At line 32, you can see we’ve used the include function of jinja to load the particular page sent by the server if the user directly goes to the url instead of clicking the specific link.
Between line 22-26, you can see there are some new attributes getting used in the links which starts with hx, let me tell you what exactly is going on over here.
- hx-get attribute sends a get request to the server endpoint specified in its value. In our case we’re sending get requests to /, /about, /contact endpoints of our flask server.
- hx-on attribute takes the trigger value, for example in our case we want the request to be sent when the element is clicked so we have used click value.
- hx-target attribute takes the element whose content needs to be replaced with the data server sent in response. In our case, we are going to replace the contents of the div with id main-container.
- hx-swap attribute takes the content type we need to replace. In our case we want to replace the innerHTML of our main-container.
- For more information, consider reading the HTMX Docs.
Now let’s create the content files which we’re going to change with the link clicks. In our case, I’m going to create three pages named services, about, contact.
services.html
<header class="bg-primary text-white text-center py-5">
<div class="container">
<h1 class="display-4 fw-bold">Welcome to Vixion</h1>
<p class="lead">Modern solutions tailored to your needs</p>
</div>
</header>
<section id="services" class="py-5">
<div class="container">
<h2 class="text-center mb-4">Our Services</h2>
<div class="row">
<div class="col-md-4 text-center mb-4">
<div class="card h-100 shadow-sm">
<div class="card-body">
<i class="bi bi-laptop fs-1 text-primary"></i>
<h5 class="card-title mt-3">Web Development</h5>
<p class="card-text">Create modern, responsive websites with cutting-edge technology.</p>
</div>
</div>
</div>
<div class="col-md-4 text-center mb-4">
<div class="card h-100 shadow-sm">
<div class="card-body">
<i class="bi bi-phone fs-1 text-primary"></i>
<h5 class="card-title mt-3">Mobile Apps</h5>
<p class="card-text">Design and develop user-friendly mobile applications.</p>
</div>
</div>
</div>
<div class="col-md-4 text-center mb-4">
<div class="card h-100 shadow-sm">
<div class="card-body">
<i class="bi bi-bar-chart fs-1 text-primary"></i>
<h5 class="card-title mt-3">Consulting</h5>
<p class="card-text">Expert guidance to enhance your business strategies.</p>
</div>
</div>
</div>
</div>
</div>
</section>
about.html
<section id="about" class="py-5">
<div class="container">
<h1 class="text-center mb-4">About Us</h1>
<div class="row align-items-center">
<div class="col-md-6">
<p>
Welcome to <strong>Vixion</strong>, where innovation meets excellence. Our team of experts is dedicated to delivering high-quality solutions that help businesses thrive in the digital age. From cutting-edge web development to user-friendly mobile applications, we craft solutions tailored to your needs.
</p>
<p>
At the heart of our mission lies a commitment to quality, integrity, and customer satisfaction. We believe in building long-term partnerships by providing reliable services that make a lasting impact.
</p>
</div>
<div class="col-md-6">
<img src="https://via.placeholder.com/500x300" alt="About Us Image" class="img-fluid rounded shadow-sm">
</div>
</div>
<div class="text-center mt-4">
<a href="/contact" class="btn btn-primary btn-lg">Contact Us</a>
</div>
</div>
</section>
contact.html
<section id="contact" class="py-5 bg-light">
<div class="container">
<h1 class="text-center mb-4">Contact Us</h1>
<p class="text-center mb-5">Have questions? Fill out the form below, and we'll get back to you as soon as possible!</p>
<div class="row justify-content-center">
<div class="col-md-8">
<form>
<div class="mb-3">
<label for="name" class="form-label">Full Name</label>
<input type="text" class="form-control" id="name" placeholder="Enter your full name" required>
</div>
<div class="mb-3">
<label for="email" class="form-label">Email Address</label>
<input type="email" class="form-control" id="email" placeholder="Enter your email" required>
</div>
<div class="mb-3">
<label for="subject" class="form-label">Subject</label>
<input type="text" class="form-control" id="subject" placeholder="Enter subject">
</div>
<div class="mb-3">
<label for="message" class="form-label">Message</label>
<textarea class="form-control" id="message" rows="5" placeholder="Write your message here..." required></textarea>
</div>
<div class="text-center">
<button type="submit" class="btn btn-primary btn-lg">Send Message</button>
</div>
</form>
</div>
</div>
</div>
</section>
After that’s done, we can move towards writing the backend. Create an app.py file and make sure that your file structure looks something like this.
templates/
- base.html
- services.html
- about.html
- contact.html
static/
app.py
Coding the Functionality of our SIngle Page Application
app.py
from flask import Flask, render_template
from flask_htmx import HTMX
app = Flask(__name__)
htmx = HTMX(app)
@app.route("/")
def home():
if htmx:
return render_template("services.html")
else:
return render_template("base.html", page="services.html")
@app.route("/about")
def about():
if htmx:
return render_template("about.html")
else:
return render_template("base.html", page="about.html")
@app.route("/contact")
def contact():
if htmx:
return render_template("contact.html")
else:
return render_template("base.html", page="contact.html")
if __name__ == "__main__":
app.run(debug=True)
In the above code you can clearly see how straightforward the process is. We just have to import flask-htmx, initialise it and then detect if the request is coming from htmx request in the route function. We’re returning the raw template in case the request is coming from htmx because we just have to replace the content of the div, the whole page is already loaded, and in the case of a direct request we’re loading the base file along with the page that needs to be displayed.
And that’s it, our single page flask app is ready. See? How easy the whole process was. Thanks for reading and stay tuned for more upcoming tutorials and guides in the future.