---
title: "Stoneybatter Cross Training App"
description: "A bespoke web application that reduced a Dublin CrossFit gym's monthly web management costs by 97%"
canonical_url: "https://tcastanie.dev/en/portfolio/stoneybatter-cross-training-app"
---

> Index of every page on this site: [llms.txt](https://tcastanie.dev/llms.txt)

# Stoneybatter Cross Training App

Web application for *Stoneybatter Cross Training*, a CrossFit gym located in Dublin, Ireland.

![Overview summary of the Stoneybatter Cross Training project](https://tcastanie.dev/portfolio/stoneybatter_social.webp)

<callout icon="i-mingcute-fast-forward-line">

By moving from a SaaS solution to a bespoke web application, the gym manager reduced their monthly management costs from €273 to €8.

</callout>

<card-group>
<card icon="i-mingcute-link-line" title="Website" target="_blank" to="https://app.stoneybattercrosstraining.ie/">

app.stoneybattercrosstraining.ie

</card>

<card icon="i-mingcute-calendar-2-line" title="Deployment">

26th February 2025

</card>

<card icon="i-mingcute-layout-11-line" title="Front-end Stack">
<badge color="neutral" variant="outline">
<icon name="i-simple-icons-nuxt">



</icon>

 Nuxt

</badge>

<badge color="neutral" variant="outline">
<icon name="i-simple-icons-vuedotjs">



</icon>

 Vue.js

</badge>

<badge color="neutral" variant="outline">
<icon name="i-simple-icons-nuxt">



</icon>

 Nuxt UI

</badge>

<badge color="neutral" variant="outline">
<icon name="i-simple-icons-typescript">



</icon>

 TypeScript

</badge>
</card>

<card icon="i-mingcute-server-2-line" title="Back-end Stack, Payments & Emails">
<badge color="neutral" variant="outline">
<icon name="i-simple-icons-directus">



</icon>

 Directus

</badge>

<badge color="neutral" variant="outline" className="mr-2">
<icon name="i-unjs-nitro">



</icon>

 Nitro

</badge>

<badge color="neutral" variant="outline" className="mr-2">
<icon name="i-simple-icons-stripe">



</icon>

 Stripe

</badge>

<badge color="neutral" variant="outline">
<icon name="i-simple-icons-amazonsimpleemailservice">



</icon>

 aws SES

</badge>

<badge color="neutral" variant="outline">

![Plunk logo](https://tcastanie.dev/portfolio/plunk-dark.svg) Plunk

</badge>
</card>
</card-group>

<note>

Access to the app is restricted to members and requires registration.

</note>

## Context

Stoneybatter Cross Training is a gym specialising in CrossFit, located in Dublin, Ireland. Classes and CrossFit sessions are offered to members who book their place in advance. Members can choose between purchasing one or several places directly, or a monthly subscription at an advantageous rate.

The gym manager was using an external *SaaS* solution to manage bookings, payments and class scheduling with coaches. The service did not meet the manager's business needs, as he was forced to send Stripe payment links via WhatsApp, manually maintain a member tracking spreadsheet, and manually follow up on failed renewal attempts.

This amounted to a notebook and pen at the cost of €273 per month.

## Overview

![Homepage screenshot](https://tcastanie.dev/stoneybatter/home.webp)

The Stoneybatter Cross Training web app allows members to purchase credits and then book a place for a CrossFit class. Classes consist of a series of exercises specific to each type of session, created by the gym's coaches. The manager and coaches have access to additional *CRUD* functionality to manage classes, *workouts* and members.

## Features

### Class calendar

![Calendar screenshot](https://tcastanie.dev/stoneybatter/calendar.webp)

### Recurring and one-off payments

![Payment page screenshot](https://tcastanie.dev/stoneybatter/paiement.webp)

### Authentication and member account management

![Account management page screenshot](https://tcastanie.dev/stoneybatter/account.webp)

![Login page screenshot](https://tcastanie.dev/stoneybatter/login.webp)

### Administration by manager and coaches

The manager and coaches have access to administrative functionality to create, modify and delete app elements, depending on their role.

![Administration example screenshot](https://tcastanie.dev/stoneybatter/admin.webp)

![Management page screenshot](https://tcastanie.dev/stoneybatter/gestion.webp)

### Automatic email sending

Automatic emails are sent to members to inform them of various events, such as booking confirmation, validation or cancellation of a waiting list booking.

### Full-screen session display

For displaying on the gym's television screen.

![TV picture](https://tcastanie.dev/stoneybatter/fullscreen.webp)

## Technical stack

The app's architecture is hosted on a server <icon name="i-mingcute-server-line" className="size-5">



</icon>

 containing the database and **Nuxt** <icon name="i-simple-icons-nuxt" className="size-5">



</icon>

.

The **SQLite** <icon name="i-simple-icons-sqlite" className="size-5">



</icon>

 database is managed via **Directus** <icon name="i-simple-icons-directus" className="size-5">



</icon>

. **Nuxt** <icon name="i-simple-icons-nuxt" className="size-5">



</icon>

 is used in *SPA* mode to ensure smooth dashboard usage and is supported by **Nuxt UI** for styling. The **Nitro** <icon name="i-unjs-nitro" className="size-4">



</icon>

 server, integrated into **Nuxt**, handles payments via **Stripe** <icon name="i-simple-icons-stripe" className="size-5">



</icon>

, serves as an intermediary between the Front-end and Backend (*BFF*), and also performs recurring tasks (cron). Automatic emails are sent via **Amazon SES** <icon name="i-simple-icons-amazonsimpleemailservice" className="size-5">



</icon>

 and their flows are managed by a **Plunk** instance. Automatic database backup is implemented with **Duplicati** <icon name="i-simple-icons-duplicati" className="size-5">



</icon>

.

## Process

Created a hidden website (IP restriction) automatically updated with each code addition to implement an adapted Agile methodology. The CrossFit gym owner was able to use and test the site regularly to provide feedback on necessary adjustments during the development phase.

On deployment day, the entire member database was imported into the app, then the site was opened to the public. Members were able to log in and use the app immediately, simply by choosing a new password.

## Maintenance and evolution

In the weeks following deployment, we made improvements and minor bug fixes reported by users.

About a month after the app stabilised, we added a new personal records (*PRs*) feature for members, allowing them to track their progress on all CrossFit exercises and compare them with other members' records, if they wish.

![Personal records leaderboard screenshot](https://tcastanie.dev/stoneybatter/prs.webp)

## Results and impact

The most striking user feedback was about the fluidity of use. With the lightweight stack and optimised queries, the app is very responsive and fast (sometimes too fast for some members used to the old solution!)

The integration of PWA features reassured some members by allowing them to install it on their phone like a native application.

Although the manager uses his computer for most administrative tasks to be more efficient, he loved being able to handle certain quick tasks from his phone when travelling, without having to postpone them.

The gym manager was also able to reduce his monthly management costs by 97%, from €273 to €8 per month, by using a bespoke solution that exactly meets his business needs.

## April 2026 Update

Update website’s technical stack and front-end redesign:

- Migration from **Nuxt** `v3` -> `v4`
- Migration from **Nuxt UI** `v2` -> `v3` -> `v4`
- Replacement of **Zod** with **Valibot**
- Upgrades to **Directus** and **Stripe**
- Complete rewrite of the front-end code to better align with business logic and deliver a faster user experience than ever before!

## Sitemap

See the full [sitemap](https://tcastanie.dev/sitemap.md) for all pages.
