Published: Jan 21, 2025 by Isaac Johnson
We’ve spoken about v0 and Lovable.dev, but today we’ll check out another big player in the space, Bolt.new. We’ll pair this with the new Free tier of Copilot (and Gemini Code Assist). After getting something working, we’ll also leverage Midjourney to build out so of our apps image before exposing it on a proper URL with TLS hosted in Kubernetes.
Signup
We can sign up with a Github account or just email
By default, we are in a Personal Plan
Pricing
As I mentioned in our writeup about Lovable.dev, its pricing is all about the Tokens
The ‘Free’ (Personal) plan gives us a generous 1M tokens which, so far, has met my basic needs.
Usage
We’ve done a lot of Resume apps lately so let’s do something a bit different. As it’s the new year, I’ve been doing a lot more workouts and I noticed with the LLMs, they are pretty good at building out fitness plans.
Let’s give Bolt.new a try in building an app we can use for various two-week plans
Create a fitness app with a landing page that offers 4 kinds of workout plans: cardio, strength, flexibility and endurance. When selecting one of the four we should ask for a level of 0 to 5 where 0 is “just starting” and 5 is “extreme”. We will use this selection along with the workout plan to then present a page showing a two-week workout cycle. For each day (1-14), present a page of a workout that would meet those criteria (type, level and a randomized workout plan). Include pictures if able. Make this site mobile friendly.
It looked like it might work, but preview didn’t work and the deploy seemed to time out - I left it paused for 15m before stopping at the end
Let me try and download
I did an npm install and npm run dev
but it’s just a blank app at this point
It does not appear to me it built any app other than the boilerplate framework
The thing is that in Bolt.new I can see a defined app.tsx so this download does not actually match the Web UI
I left Bolt and came back in and the App.tsx seems to sadly have reset
The preview works and matches what I saw locally.
reducing
I’m going to try again, but simplify my prompt to just the initial selection for now
Create a fitness app with a landing page that offers 4 kinds of workout plans: cardio, strength, flexibility and endurance. When selecting one of the four we should ask for a level of 0 to 5 where 0 is “just starting” and 5 is “extreme”. Keep the style modern and use fitness images where able.
I can see it built an app
But despite what it says, there is no preview
I’ll try deploy again.. but still times out.
Trying locally it did show something different - an error
One issue I corrected was a lack of a “Yoga” icon from lucide.
builder@DESKTOP-QADGF36:/mnt/c/Users/isaac/Downloads/project-bolt-sb1-3u2gayjm/project$ npm run build
> vite-react-typescript-starter@0.0.0 build
> vite build
vite v5.4.8 building for production...
✓ 1470 modules transformed.
x Build failed in 31.85s
error during build:
src/App.tsx (5:2): "Yoga" is not exported by "node_modules/lucide-react/dist/esm/lucide-react.js", imported by "src/App.tsx".
file: /mnt/c/Users/isaac/Downloads/project-bolt-sb1-3u2gayjm/project/src/App.tsx:5:2
3: Heart,
4: Dumbbell,
5: Yoga,
^
6: Timer,
7: ChevronLeft,
...
I went to the Lucide library and replaced “Yoga” with “tangent” which seemed similar.
That’s now looking much better
I went back and made the corrections in Bolt after refreshing (to stop the broken build). This was namely:
- comment out the trailing “export”
- set the App to have a “default export”
- replace the “Yoga” icon with “Tangent”
Then I did a “deploy” and saw a resulting preview page
I’ll now ask to build out just strength training.
This is not a bad workout. We can see it has a decent 7-day plan and while it did put in some good fitness images, they don’t actually match the workout step. They are pretty darn close though and for a free image, I think it’s a good start.
I’ll now politely ask it to do the same for Cardio
That looks great, thank you. Please do the same for the cardio page.
It gave an error but also prompted me to let it fix the error.
Not perfect, but not too shabby.
I’ll keep going:
Fantastic. Please do the same for the Flexibility section which should create a workout with stretches and yoga with a focus on mobility.
Ahh! They got me.
That said, I can download and see what we can do from there.
Since I plan to proceed with VS Code in WSL, I’ll move it into my Linux filesystem
builder@DESKTOP-QADGF36:~/Workspaces$ mv /mnt/c/Users/isaac/Downloads/project-bolt-sb1-3u2gayjm_new/project ./BoltFitnessApp
builder@DESKTOP-QADGF36:~/Workspaces$
builder@DESKTOP-QADGF36:~/Workspaces$ cd BoltFitnessApp/
builder@DESKTOP-QADGF36:~/Workspaces/BoltFitnessApp$ ls
dist index.html package.json src tsconfig.app.json tsconfig.node.json
eslint.config.js package-lock.json postcss.config.js tailwind.config.js tsconfig.json vite.config.ts
Next, I’ll create a repo in Github and since I really want this Open-Source, I’ll make it public
I can then push it up to Github
builder@DESKTOP-QADGF36:~/Workspaces/BoltFitnessApp$ git init
Initialized empty Git repository in /home/builder/Workspaces/BoltFitnessApp/.git/
builder@DESKTOP-QADGF36:~/Workspaces/BoltFitnessApp$ git add *.*
builder@DESKTOP-QADGF36:~/Workspaces/BoltFitnessApp$ git add src
builder@DESKTOP-QADGF36:~/Workspaces/BoltFitnessApp$ git commit -m "First"
[master (root-commit) 866e3f1] First
14 files changed, 4856 insertions(+)
create mode 100755 eslint.config.js
create mode 100755 index.html
create mode 100755 package-lock.json
create mode 100755 package.json
create mode 100755 postcss.config.js
create mode 100755 src/App.tsx
create mode 100755 src/index.css
create mode 100755 src/main.tsx
create mode 100755 src/vite-env.d.ts
create mode 100755 tailwind.config.js
create mode 100755 tsconfig.app.json
create mode 100755 tsconfig.json
create mode 100755 tsconfig.node.json
create mode 100755 vite.config.ts
builder@DESKTOP-QADGF36:~/Workspaces/BoltFitnessApp$ git branch -M main
builder@DESKTOP-QADGF36:~/Workspaces/BoltFitnessApp$ git remote add origin https://github.com/idjohnson/BoltFitnessApp.git
builder@DESKTOP-QADGF36:~/Workspaces/BoltFitnessApp$ git push -u origin main
Enumerating objects: 17, done.
Counting objects: 100% (17/17), done.
Delta compression using up to 16 threads
Compressing objects: 100% (16/16), done.
Writing objects: 100% (17/17), 40.31 KiB | 10.08 MiB/s, done.
Total 17 (delta 1), reused 0 (delta 0)
remote: Resolving deltas: 100% (1/1), done.
To https://github.com/idjohnson/BoltFitnessApp.git
* [new branch] main -> main
Branch 'main' set up to track remote branch 'main' from 'origin'.
I like using VSCode Locally, but one of my new favourite things to use with Github is Codespaces.
I’ll show you how that works while also showing how to add in Copilot to Codespaces
I’ll now ask Copilot via Codespaces what I would have asked Bolt - please add the next section:
App.tsx has defined a workoutPlan for cardio (getCardioPlan) and strength (getStrengthPlan). Please add one for flexibility similar to the ones mentioned.
It kind of worked, but not as I had hoped
I asked it again
Can you try again? I want all the same features including getting a level and using it as a multiplier and returning an week of workouts starting with Monday
This did better but I will still have to massage it a bit more
I tried to nudge it a bit more:
Please pay attention to the Exercise interface at line 19. The return should be a collection of 7 WorkoutDay instances, one for each day of the week (Monday, Tuesday, Wednesday, Thursday, Friday, Saturday, Sunday). Each Workoutday then creates a collection of Exercises
This time I at least have something with which I can work:
I knew there were missing fields so I tried to highlight the block and say “fix”. This did a good job of adding in all the missing fields, but didn’t handle the formatting
I then took about 20 minutes to fix the formatting slowly (just adding new lines) then going through and fixing the durations to use a proper function like cardio did. I also went to Unsplash to look up some free images that are close the defined stretches
I made sure to save my work
no changes added to commit (use "git add" and/or "git commit -a")
@idjohnson ➜ /workspaces/BoltFitnessApp (main) $ git add src/App.tsx
@idjohnson ➜ /workspaces/BoltFitnessApp (main) $ git commit -m "updates"
[main a514efe] updates
1 file changed, 150 insertions(+), 29 deletions(-)
@idjohnson ➜ /workspaces/BoltFitnessApp (main) $
I had a few fixes yet to do, but once I could compile, I used ‘npm run preview’ to fire up a server and Codespaces the prompted me to a URL to view the app
As you can see it mostly works, but the durations are not showing
I fixed the issue with the duration and then added a rest image
I’ll then just stash the changes for the moment
no changes added to commit (use "git add" and/or "git commit -a")
@idjohnson ➜ /workspaces/BoltFitnessApp (main) $ git add src/App.tsx
@idjohnson ➜ /workspaces/BoltFitnessApp (main) $ git commit -m "fix"
[main 24ad588] fix
1 file changed, 81 insertions(+), 20 deletions(-)
@idjohnson ➜ /workspaces/BoltFitnessApp (main) $ git push
Enumerating objects: 11, done.
Counting objects: 100% (11/11), done.
Delta compression using up to 2 threads
Compressing objects: 100% (8/8), done.
Writing objects: 100% (8/8), 1.52 KiB | 1.52 MiB/s, done.
Total 8 (delta 4), reused 0 (delta 0), pack-reused 0 (from 0)
remote: Resolving deltas: 100% (4/4), completed with 2 local objects.
To https://github.com/idjohnson/BoltFitnessApp
866e3f1..24ad588 main -> main
I came back later and brought down the code locally to keep going.
I decided this time to ask Gemini Code Assist on how I might fix the images to not reference external URLs
I then asked Gemini and Copilot at the same time to help me gather the images. Gemini took longer but did deliver me the full list so it was more complete
I changed just the first, the bench press (though I downloaded all the images) and it worked great
This was way faster in loading, and for the new images it worked great
But the automated ones had some broken images and some that just didn’t make a whole lot of sense. For instance, cycling was just a picture of space
Midjourney
I now want to replace these pretty images with some Isometric generated art.
I tried a few options, but settled on, for instance
modern illustration of a deadlift exercise, isometric with white background
In testing, I found the images appear a bit cropped
I spent several hours using MidJourney to build out some decent images. Here is just a snippet of me trying to build one isometric exercise
Dockerfile
I next asked Copilot to help build out a Dockerfile. It actually refused to help me if I included the package.json but it would work just fine when I removed it
But I could not get that to work. So I then tried Gemini Code Assist which seemed to have a better idea
It wasn’t perfect, but not far off. I fixed it by commenting out the ci
step and naming the builder container
FROM node:20-alpine AS builder
WORKDIR /app
COPY package.json package-lock.json ./
#RUN npm ci --omit=dev
COPY . .
RUN npm run build
FROM node:20-alpine
WORKDIR /app
COPY --from=builder /app/dist ./dist
COPY package.json package-lock.json ./
#RUN npm ci --omit=dev --production
EXPOSE 5173
CMD ["npx", "vite", "preview", "--port", "5173", "--host"]
Which ran locally without issue
Let’s see if we can get Copilot to build out a Github workflow
I put it in the folder it would need to be in, namely .github/workflows
name: Build and Push Docker Image
on:
push:
branches:
- main
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v3
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2
- name: Log in to GitHub Container Registry
uses: docker/login-action@v2
with:
registry: ghcr.io
username: $
password: $
- name: Build and push Docker image
uses: docker/build-push-action@v4
with:
context: .
push: true
tags: ghcr.io/$/boltfitnessapp:latest
While it did build, the error shows us that we need a proper GH Token for logging in
That was just due to a missing permission on the workflow file
which worked
Now we have an image at
ghcr.io/idjohnson/boltfitnessapp:latest
I’ll now ask Copilot to create the Kubernetes manifest
create a kubernetes manifest that creates a deployment with the image ghcr.io/idjohnson/boltfitnessapp:latest. it should always pull a fresh image. It should expose port 5173 and have 2 replicas. The manifest should also include a service to expose the deployment that is of type clusterip
Let’s now try to deploy to k8s
$ kubectl apply -f manifest.yaml
deployment.apps/boltfitnessapp-deployment created
service/boltfitnessapp-service created
A quick port-forward works
builder@LuiGi:~/Workspaces/BoltFitnessApp$ kubectl get svc boltfitnessapp-service
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
boltfitnessapp-service ClusterIP 10.43.116.26 <none> 5173/TCP 79s
builder@LuiGi:~/Workspaces/BoltFitnessApp$ kubectl port-forward svc/boltfitnessapp-service 5173:5173
Forwarding from 127.0.0.1:5173 -> 5173
Forwarding from [::1]:5173 -> 5173
Handling connection for 5173
Handling connection for 5173
Handling connection for 5173
So let’s finish this phase with a proper ingress. I’ll create an A Record
$ gcloud dns --project=myanthosproject2 record-sets create boltfit.steeped.spac
e --zone="steepedspace" --type="A" --ttl="300" --rrdatas="75.73.224.240"
NAME TYPE TTL DATA
boltfit.steeped.space. A 300 75.73.224.240
So I then went to use it
builder@LuiGi:~/Workspaces/BoltFitnessApp$ cat ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
annotations:
cert-manager.io/cluster-issuer: gcpleprod2
ingress.kubernetes.io/proxy-body-size: "0"
ingress.kubernetes.io/ssl-redirect: "true"
kubernetes.io/ingress.class: nginx
kubernetes.io/tls-acme: "true"
nginx.ingress.kubernetes.io/proxy-body-size: "0"
nginx.ingress.kubernetes.io/proxy-read-timeout: "3600"
nginx.ingress.kubernetes.io/proxy-send-timeout: "3600"
nginx.ingress.kubernetes.io/ssl-redirect: "true"
nginx.org/client-max-body-size: "0"
nginx.org/proxy-connect-timeout: "3600"
nginx.org/proxy-read-timeout: "3600"
nginx.org/websocket-services: boltfitnessapp-service
name: boltfitnessapp
spec:
rules:
- host: boltfit.steeped.space
http:
paths:
- backend:
service:
name: boltfitnessapp-service
port:
number: 5173
path: /
pathType: ImplementationSpecific
tls:
- hosts:
- boltfit.steeped.space
secretName: boltfitnessapp-tls
builder@LuiGi:~/Workspaces/BoltFitnessApp$ kubectl apply -f ./ingress.yaml
ingress.networking.k8s.io/boltfitnessapp created
So then I can apply it
$ kubectl get cert boltfitnessapp-tls
NAME READY SECRET AGE
boltfitnessapp-tls True boltfitnessapp-tls 78s
Which now has a functioning app exposed via TLS
Summary
Today we used Bolt.new to create a brand-new fitness app and took it as far as the free tier allowed us. I then used the new Free tier of Copilot to help build out more of the app along with Gemini Code Assist. We tied in Midjourney for image generation and wrapped by, again, using Copilot to help build out a Kubernetes manifest.
I would likely look to a better way to do images and the Midjourney approach did take plenty of time. I also would want to use something to build GIF images or some form of animations next. I also left the Endurance Training empty for a second pass later.
Overall, this was none too hard, but there were some challenges that required manual intervention and code corrections.
The app itself is totally Open-Source, so if you desire to expand it or fork it, by all means take and use. The repository is here: