Skip to main content

How to build a Modern Landing Page with Flask and Bootstrap?

In this tutorial, we will create a modern and responsive landing page for a pickleball club using Frozen-Flask and Bootstrap 5, featuring a navigation bar, hero banner, about section, facilities showcase, contact form integration, and footer. This approach combines the simplicity of Flask development with the performance and cost benefits of static site hosting, making it an excellent choice for clubs, portfolios, small businesses, and promotional websites.

Prerequisite:

This tutorial is part of the Flask Landing Page and Reservation System Series.

📚 View the Complete Flask Landing Page and Reservation System Series

                                                                                                                                              ➡ Next Part

Preliminary:

Before I begin, please activate the virtual environment and install the required dependencies.
python -m venv venv
venv\Scripts\activate
pip install flask frozen-flask
Then, we need to set up the file and folder structure, as below:
I need to store all the images under the static and assets folders, including 
  • about.png, 
  • changing_rooms.png, 
  • court.png, 
  • free_parking.png, 
  • hero.png, 
  • logo.png, and 
  • night_lighting.png
While app.py and freeze.py are my working files.

Why use Frozen-Flask to build a landing page?
Flask is traditionally used for dynamic websites; it communicates between the browser and the server using the Jinja2 template engine. However, frozen-flask is a tool to "freeze" a dynamic app into a static app, simply a few lines of code. 

The benefits are shown below: 
1) Minimal Hosting Cost—the static file can be hosted on any static site cloud with no cost, such as a GitHub page, Netlify, or an Amazon S3 bucket. 
2) No database required—the static file does not need to maintain a database to store the user data; therefore, no security issues, including hackers or ransomware attacks. 
3) Performance and Scalability—static files load faster and are capable of handling heavy traffic of visitors. 
4) Smart URL link—thanks to Jinja2, it helps to map to every page. Even though the HTML is "frozen" to a static file, every link remains intact. 

Therefore, in this tutorial, I will create a normal Flask website and later on "freeze" it into a static site.

Why use Bootstrap 5?
As you have noticed, my website is not only viewed on a desktop or laptop! This is a result of using Bootstrap 5 for a responsive view on any device, including desktop, tablet, or mobile devices. Once I use Bootstrap 5, I need not code for small-screen devices, and the code will take care of it. Simply copy these 2 lines of code below:
CSS:
<link rel="stylesheet"
     href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css">
JS:
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3
/dist/js/bootstrap.bundle.min.js">
</script>
Custom CSS
This will be connected to my static folder and style.css file.
<link rel="stylesheet"
          href="{{ url_for('static', filename='style.css') }}">

Step 1: Create a normal Flask application
First and foremost, create a folder and a basic Flask web app as usual. So, in my bash terminal, I input the following line:
mkdir JCPC
cd JCPC
touch app.py
Next, in my app.py file, I will create a few lines of code as below:
from flask import Flask, render_template

app = Flask(__name__)

@app.route('/')
def index():
    return render_template('index.html')

if __name__ == '__main__':
    app.run(debug=True)
Since the landing page is a single page, I only need a route.

Step 2: Create a template, a static folder
Before creating files, I need to set up the folders and files as the following lines in the bash terminal:
mkdir templates
cd templates
touch index.html
cd ..
mkdir static
cd static touch style.css mkdir assets
cd..
The folder structure is as shown in the preliminary section above. Once the assets folder has been created, go ahead and copy the images to the assets folder.

Step 3: Configure the index.html and style.css files for every section
In my landing page, I have divided it into the following 5 sections:
  1. Navigation and hero section
  2. About section
  3. Facilities section
  4. Contact form
  5. Footer section
(1) Navigation bar and hero section 
The top navigation displays the logo and title of the website. While the Hero section is the most amazing part, where users are attracted to surf further. This part included an image, a heading, a subheading, and a call-to-action button.

So, my code is in 2 files, one in style.css (custom stylesheet) under the static folder and another in index.html under the templates folder as follows:

(a) style.css
The background image is sourced from the 'static/assets' folder.
html,
body {
    overflow-x: hidden;
    width: 100%;
    background: #3f496a;
}

.h3  {
    color: #ffc107;
}

.hero {
    min-height: 500px;
    height: 70vh;

    background:
        linear-gradient(
            rgba(0,0,0,.5),
            rgba(0,0,0,.5)
        ),
        url("../static/assets/hero.png");

    background-size: cover;
    background-position: center center;
}

.hero-overlay {
    height: 100%;
    display: flex;
    align-items: center;
    justify-content: center;
}
.hero-overlay {
    height: 100%;
    display: flex;
    align-items: center;
    justify-content: center;
}

.lead {
    color: white;
    font-size: 25px;
    font-weight: bold;
}

.section-divider {
    height: 2px;
    background: #dee2e6;
    margin: 40px 0;
}
(b) index.html
On top of the section containing the stylesheet, besides linking to Bootstrap 5, I also code a custom CSS under the 'static/style.css' file.

The button is currently left blank. When the reservation web app is ready, it will be linked to this button later on.
<link rel="stylesheet"
     href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css">
<link rel="stylesheet"
     href="{{ url_for('static', filename='style.css') }}">


<!-- Navigation -->
<nav class="navbar navbar-dark bg-dark">
  <div class="container-fluid">
    <a class="navbar-brand d-flex align-items-center" href="#">
      <img src="static/assets/logo.png" alt="JCPC Logo" 
      width="30" height="30" class="d-inline-block align-text-top me-2">
      <span class="h3 mb-0">Jersey City Pickleball Club</span>
    </a>
  </div>
</nav>

<!---hero section--->
<section class="hero">
    <div class="hero-overlay">
        <div class="container text-center text-white">
            <h1 class="display-3 fw-bold">
                Jersey City Pickleball Club
            </h1>

            <p class="lead">
                Play. Compete. Connect.
            </p>

            <a href="/#" class="btn btn-warning btn-lg">
                Book a Court
            </a>
        </div>
    </div>
</section
<hr class="section-divider">

(2) About section:
 
This is an introduction to who we are and what we do, so in this tutorial, I wrote a simple introduction to what the Jersey City Pickleball Club offers to the community. 

 (a) style.css
h2 { color: white; 
    font-size: 48px; 
    font-weight: bold; 
    text-shadow: 2px 2px 4px rgba(0,0,0,0.5);
    text-align: center;
    margin: 0;}

p {
    text-align: center;
    margin-top: 20px;
}

.section-divider {
    height: 2px;
    background: #dee2e6;
    margin: 40px 0;
}
(b) index.html
The image is sourced from the static/assets folder.
<!---about section--->
<section id="about" class="section-padding">
    <div class="row align-items-center"> 
        <div class="col-lg-6"> 
            <img src="{{ url_for('static', filename='assets/about.png') }}" 
            class="img-fluid rounded shadow" alt="About Image"> 
        </div> 

        <div class="col-lg-6"> 
            <h2>About Us</h2> 
            <p> 
                Jersey City Pickleball Club is a vibrant community where players of 
                all skill levels come together to enjoy pickleball. 
            </p> 
            <p> 
                Whether you're a beginner or an experienced competitor, you'll find 
                a welcoming place to play, improve, and connect with others. 
            </p> 
        </div> 
    </div>
</section>
<hr class="section-divider">
Final Wrap-up: 
In this tutorial, we explored why Frozen-Flask is an excellent choice for building fast, lightweight, and easy-to-deploy static websites while still benefiting from Flask's familiar development workflow. We also leveraged Bootstrap 5 to create a responsive and professional-looking user interface and demonstrated how to build two essential landing page components: a navigation bar and a hero section, followed by a content section. These foundational elements provide the structure and styling needed for a modern landing page. In the next part of this project, we will continue enhancing the website by adding a facilities showcase, a contact section, and a footer to complete the landing page experience. 

Published: June 2026
Last Updated: June 2026

About the Author

Kelvin Loh is a Python developer focused on Flask, desktop applications, and business automation solutions. He shares practical tutorials and real-world coding projects to help developers and small businesses build useful applications.


Comments

Popular Posts

How to Design a Location Tracking Module for Desktop Business Systems Using Python?

This tutorial will show you how to create an interactive map application that allows users to input geographic coordinates and visualize locations on an interactive map using Python's Tkinter GUI framework enhanced with TTKBootstrap styling and the TkinterMapView widget.  Prerequisite: This tutorial is part of the standalone tutorial. 📚 View the  standalone tutorial Preliminary   Before I begin, it is recommended to activate the virtual environment before installing the relevant dependencies. python -m venv venv venv\Scripts\activate pip install ttkbootstrap tkintermapview As usual, we need to import the relevant module and set the root values from tkinter import * import ttkbootstrap as tb import tkintermapview from ttkbootstrap.dialogs import Messagebox root = tb.Window(themename='darkly') root.title('Find My Map') root.geometry("1200x1200") I would like to divide it into three sections.  a) Input Panel Layout,  The headers are mainly a label...

How to Create Flask Forms with CKEditor and Flask-WTF?

In this tutorial, we will be integrating Flask-WTF and Flask-CKEditor into your Flask application! You'll learn how to set up the editor, securely handle formatted HTML content, and create a seamless user experience that enhances any project that requires user-generated content. Let's get started! Prerequisite: This tutorial is part of the Flask CKEditor Project Series. 📚 View the Complete Flask CKEditor Series                                                                                                                                                  ➡ Next Part Preliminary Before I begin, it is recommended to activate the v...

How to Store Application Data with SQLite in Python?

  In the previous tutorial, I used TinyDB as our storage solution to keep application data in a simple JSON-based format. While TinyDB is lightweight and easy to use, many web applications require a more structured and scalable database system. In this tutorial, we will switch to SQLite , a powerful relational database that integrates well with Python and web frameworks. By using SQLite, you will learn how to store, manage, and query data in a more structured way for real-world applications. Prerequisite: This tutorial is part of the Flask CKEditor Project Series. 📚 View the Complete Flask CKEditor Series ⬅ Previous Part                                                                                                         ...