How to make a navigation bar in ReactPy

reactpy navigation bar
Updated: August 15, 2023

A. Introduction

Navigation bar helps the users find quickly the contents that they want to see. A well designed navigation elements will result to a good user experience.

B. What we are building

We will create 2 buttons on the horizontal bar. The home and blog buttons. Each button has a link that will bring the user to a specific url or address. I use bootstrap 5.2 to style the elements.

1. Home page

This is the home page, its url is:

http://127.0.0.1:8000

2. Blog page

This is the blog page, its url is:

http://127.0.0.1:8000/blog

The word blog is also called the path.

3. Small Screen

The navbar adjusts to devices with small screen like mobile phones. It arranges the bar items vertically and add a button that toggles to hide and unhide the bar items.
Bootstrap's site has an example html code to build a navbar. We will build our navbar based from this example.

The following reactpy code will generate html output similar to that bootstrap code. But this has only home and blog buttons.

@component
def NavBar(nav_attr: dict):
    return html.nav(
        {'class': 'navbar navbar-dark navbar-expand-lg bg-dark'},
        html.div(
            {'class': 'container-fluid'},
            html.a({'class': 'navbar-brand text-primary', 'href': '#'},
                   'ReactPy-Navbar'),
            html.button(
                {
                    'class': 'navbar-toggler',
                    'type': 'button',
                    'data-bs-toggle': 'collapse',
                    'data-bs-target': '#navbarSupportedContent',
                    'aria-controls': 'navbarSupportedContent',
                    'aria-expanded': 'false',
                    'aria-label': 'Toggle navigation',
                },
                html.span({'class': 'navbar-toggler-icon'}),
            ),
            html.div(
                {'class': 'collapse navbar-collapse',
                 'id': 'navbarSupportedContent'},
                html.ul(
                    {'class': 'navbar-nav me-auto mb-2 mb-lg-0'},
                    NavItem('Home', '/', nav_attr.get('Home', False)),
                    NavItem('Blog', '/blog', nav_attr.get('Blog', False)),
                ),
            ),
        ),
    )

We also need the bootstrap CSS and script in order for this navbar to work.

BOOTSTRAP_CSS = html.link(
    {
        'rel': 'stylesheet',
        'href': 'https://cdn.jsdelivr.net/npm/bootstrap@5.2.3/'
                'dist/css/bootstrap.min.css',
        'integrity': 'sha384-rbsA2VBKQhggwzxH7pPCaAqO46MgnOM80'
                     'zW1RWuH61DGLwZJEdK2Kadq2F9CUG65',
        'crossorigin': 'anonymous'
    }
)

BOOTSTRAP_SCRIPT = html.script(
    {
        'src': 'https://cdn.jsdelivr.net/npm/bootstrap@5.2.3/'
               'dist/js/bootstrap.bundle.min.js',
        'integrity': 'sha384-kenU1KFdBIe4zVF0s0G1M5b4hcpxyD9'
                     'F7jL+jjXkk+Q2h455rYXK/7HAuoJl+0I4',
        'crossorigin': 'anonymous'
    }
)

D. Requirements

requirements.txt
reactpy[fastapi]
reactpy-router
uvicorn[standard]

E. Code Snippets

There is a library called reactpy-route that can be used to create a link that allows the navigation buttons to work.

1. The root component

@component
def root():
    return simple.router(
        route('/', Home()),
        route('/blog', Blog()),
        route('*', MissingLink()),
    )

Visiting the url such as http://127.0.0.1:8000 will call the Home() component. Navigating to http://127.0.0.1:8000/blog executes the Blog() component. For other paths such as http://127.0.0.1:8000/pricing, MissingLink() will be executed.

2. The home component

@component
def Home():
    return html.div(
        BOOTSTRAP_CSS,
        BOOTSTRAP_SCRIPT,
        html.div(
            {'class': 'container mt-3'},
            NavBar({'Home': True}),

            # Home content starts here.
            ######################################################
            html.div(
                html.h5({'class': 'my-3'}, 'Home'),
                html.h2('The Food supply'),
                html.p(
                    '''Lorem ipsum dolor sit amet, consectetur
                       adipiscing elit. Suspendisse volutpat nisi
                       quis ligula lobortis, eget venenatis elit.
                       Integer vel nunc at felis tristique faucibus.
                       Nullam id est quis sapien aliquet tincidunt.'''
                ),
                html.p(
                    '''In euismod massa a aliquet posuere. Ut tristique
                       libero euismod ex feugiat consectetur. Maecenas
                       ultricies, justo sit amet rhoncus sagittis, nisi
                       eros venenatis nulla, ut vestibulum odio turpis
                       nec dolor. Vivamus lobortis.'''
                ),
            ),
        ),
    )

3. The blog component

@component
def Blog():
    return html.div(
        BOOTSTRAP_CSS,
        BOOTSTRAP_SCRIPT,
        html.div(
            {'class': 'container mt-3'},
            NavBar({'Blog': True}),

            # Blog content starts here.
            ######################################################
            html.div(
                html.h5({'class': 'my-3'}, 'Blog'),
                html.h2('The good news in year 2025'),
                html.p(
                    '''Lorem ipsum dolor sit amet, consectetur adipiscing
                       elit. Fusce a nisi vitae urna posuere iaculis.
                       Suspendisse eget lorem a velit ultricies sodales.'''
                ),
                html.p(
                    '''Sed venenatis magna eget ipsum fringilla, nec
                       dignissim nisi interdum. Duis ut dapibus orci.
                       In lobortis elit sit amet semper.'''
                ),
            ),
        ),
    )

F. Full code

navbar.py
"""Creates a sample navigation bar with ReactPy and Bootstrap 5.

Builds components with ReactPy and styled by Bootstrap. ReactPy-Router
is used to generate routes for the navbar buttons. The navbar design
is responsive to both desktop and mobile screens.
"""


from reactpy import component, html
from reactpy.backend.fastapi import configure, Options
from reactpy_router import route, simple
from fastapi import FastAPI


BOOTSTRAP_CSS = html.link(
    {
        'rel': 'stylesheet',
        'href': 'https://cdn.jsdelivr.net/npm/bootstrap@5.2.3/'
                'dist/css/bootstrap.min.css',
        'integrity': 'sha384-rbsA2VBKQhggwzxH7pPCaAqO46MgnOM80'
                     'zW1RWuH61DGLwZJEdK2Kadq2F9CUG65',
        'crossorigin': 'anonymous'
    }
)

BOOTSTRAP_SCRIPT = html.script(
    {
        'src': 'https://cdn.jsdelivr.net/npm/bootstrap@5.2.3/'
               'dist/js/bootstrap.bundle.min.js',
        'integrity': 'sha384-kenU1KFdBIe4zVF0s0G1M5b4hcpxyD9'
                     'F7jL+jjXkk+Q2h455rYXK/7HAuoJl+0I4',
        'crossorigin': 'anonymous'
    }
)

HEADER_TITLE = 'ReactPy-Navbar'


@component
def NavItem(label: str, path: str, is_active: bool = False):
    attribute = {'href': path}
    if is_active:
        attribute.update(
            {'class': 'nav-link active', 'aria-current': 'page'},
        ),
    else:
        attribute.update(
            {'class': 'nav-link'},
        ),

    return html.div(
        html.li(
            {'class': 'nav-item'},
            html.a(attribute, label),
        ),
    )


@component
def NavBar(nav_attr: dict):
    return html.nav(
        {'class': 'navbar navbar-dark navbar-expand-lg bg-dark'},
        html.div(
            {'class': 'container-fluid'},
            html.a({'class': 'navbar-brand text-primary', 'href': '#'},
                   'ReactPy-Navbar'),
            html.button(
                {
                    'class': 'navbar-toggler',
                    'type': 'button',
                    'data-bs-toggle': 'collapse',
                    'data-bs-target': '#navbarSupportedContent',
                    'aria-controls': 'navbarSupportedContent',
                    'aria-expanded': 'false',
                    'aria-label': 'Toggle navigation',
                },
                html.span({'class': 'navbar-toggler-icon'}),
            ),
            html.div(
                {'class': 'collapse navbar-collapse',
                 'id': 'navbarSupportedContent'},
                html.ul(
                    {'class': 'navbar-nav me-auto mb-2 mb-lg-0'},
                    NavItem('Home', '/', nav_attr.get('Home', False)),
                    NavItem('Blog', '/blog', nav_attr.get('Blog', False)),
                ),
            ),
        ),
    )


@component
def Home():
    return html.div(
        BOOTSTRAP_CSS,
        BOOTSTRAP_SCRIPT,
        html.div(
            {'class': 'container mt-3'},
            NavBar({'Home': True}),

            # Home content starts here.
            ######################################################
            html.div(
                html.h5({'class': 'my-3'}, 'Home'),
                html.h2('The Food supply'),
                html.p(
                    '''Lorem ipsum dolor sit amet, consectetur
                       adipiscing elit. Suspendisse volutpat nisi
                       quis ligula lobortis, eget venenatis elit.
                       Integer vel nunc at felis tristique faucibus.
                       Nullam id est quis sapien aliquet tincidunt.'''
                ),
                html.p(
                    '''In euismod massa a aliquet posuere. Ut tristique
                       libero euismod ex feugiat consectetur. Maecenas
                       ultricies, justo sit amet rhoncus sagittis, nisi
                       eros venenatis nulla, ut vestibulum odio turpis
                       nec dolor. Vivamus lobortis.'''
                ),
            ),
        ),
    )


@component
def Blog():
    return html.div(
        BOOTSTRAP_CSS,
        BOOTSTRAP_SCRIPT,
        html.div(
            {'class': 'container mt-3'},
            NavBar({'Blog': True}),

            # Blog content starts here.
            ######################################################
            html.div(
                html.h5({'class': 'my-3'}, 'Blog'),
                html.h2('The good news in year 2025'),
                html.p(
                    '''Lorem ipsum dolor sit amet, consectetur adipiscing
                       elit. Fusce a nisi vitae urna posuere iaculis.
                       Suspendisse eget lorem a velit ultricies sodales.'''
                ),
                html.p(
                    '''Sed venenatis magna eget ipsum fringilla, nec
                       dignissim nisi interdum. Duis ut dapibus orci.
                       In lobortis elit sit amet semper.'''
                ),
            ),
        ),
    )


@component
def MissingLink():
    return html.div(
        BOOTSTRAP_CSS,
        html.div(
            {'class': 'container mt-3'},
            html.h1(html.h1("Missing Link 🔗‍💥"))
        ),
    )


@component
def root():
    return simple.router(
        route('/', Home()),
        route('/blog', Blog()),
        route('*', MissingLink()),
    )


app = FastAPI()
configure(
    app,
    root,
    options=Options(
        head=html.head(html.title(HEADER_TITLE))
    )
)

The source code is also in my github repository. See Github ReactPy-Navbar in the reference section.

You can run the app with the following command line.

uvicorn navbar:app

G. Summary

Navigation bar or navbar provides comforts to users in exploring the sites and pages. Navbar is created using ReactPy and Bootstrap 5. It is responsive and can handle both the big and small screens.

H. References