Building the Same App across Various Web Frameworks

Building the Same App across Various Web Frameworks


I’ve been building simple web apps using a combination of FastAPI, HTML templates, CSS, and a bit of JavaScript. Recently, I was curious if web frameworks could make this easier. Thus, I ran polls asking about a few frameworks.

  • FastHTML: I’ve heard several good things about it since Jeremy Howard announced it a month ago. The goal is to enable modern web applications in pure Python.
  • Next.js: I’ve used several apps built in Next.js such as cal.com and roomGPT. It has a large ecosystem and is popular for building production-grade web apps.
  • SvelteKit: This lightweight framework has been popular with developers in the Stack Overflow, TSH, and State of JS surveys over the past few years.

Polls on Twitter and LinkedIn

To get a better feel of these frameworks, I built the same simple web app using each. The app, which I’m calling “Look at Your Data”, allows users to do the following:

  • Upload a CSV file to initialize an SQLite database table
  • View the table in the web browser
  • Update individual fields in the table
  • Delete individual rows in the table
  • Download the updated table data as a new CSV file

By implementing these CRUD (Create, Read, Update, Delete) operations in each framework, I hope to get a sense of the developer experience while building a web app that relies on a simple database (we’ll use SQLite here).

As a baseline, I’ll start by building the app with what I’m familiar with: FastAPI.

FastAPI + Jinja templates + HTML + CSS + JavaScript

Building the app with FastAPI is fairly straightforward (code). The key components are:

  • main.py: Routes for uploading/downloading data, updating fields, deleting rows
  • index.html: Defines the HTML document using Jinja templating
  • style.css: Controls visual styling such as column widths and word wrapping
  • script.js: Client-side functionality for uploading the CSV, loading data, updating/deleting rows, etc.

FastAPI app structure

And here’s what the web app looks like. While it’s not much to look at aesthetically, the web app meets our requirements above. I’ve deliberately kept visual styling to a minimum (for the current and later apps) to keep the focus on the frameworks and functionality rather than the design.

FastAPI web app

FastHTML

To learn FastHTML, I started by consulting the docs and following a walkthrough of building a ToDo app. For help with unfamiliar components, I relied on Cursor by providing it with links to relevant docs such as ft components, htmx, pico.css, etc. With FastHTML, I implemented the entire app within a single main.py and a small style.css to control column widths and word wrapping (code).

FastHTML app structure

And here’s how the app looks.

FastHTML web app

After my first iteration (above), Hamel graciously offered to pair-program with me and rebuild the app from scratch. Jeremy Howard, the creator of FastHTML himself, joined us. They taught me several tricks, such as providing Cursor with LLM-friendly documentation for FastHTML (llms-ctx.txt) and FastLite (html.md). They also shared this free book on building simpler apps with htmx and Hyperview. Jeremy even took the time to demonstrate how to build the app in just 50 lines of code .

from fasthtml.common import *

db = database(':memory:')
tbl = None
hdrs = (Style('''
button,input { margin: 0 1rem; }
[role="group"] { border: 1px solid #ccc; }
'''), )
app, rt = fast_app(live=True, hdrs=hdrs)

@rt("https://eugeneyan.com/")
async def get():
    return Titled("CSV Uploader",
        Group(
            Input(type="file", name="csv_file", accept=".csv"),
            Button("Upload", hx_post="/upload", hx_target="#results",
                   hx_encoding="multipart/form-data", hx_include='previous input'),
            A('Download', href='/download', type="button")
        ),
        Div(id="results"))

def render_row(row):
    vals = [Td(Input(value=v, name=k)) for k,v in row.items()]
    vals.append(Td(Group(Button('delete', hx_get=remove.rt(id=row['id'])),
                   Button('update', hx_post='/update', hx_include="closest tr"))))
    return Tr(*vals, hx_target='closest tr', hx_swap='outerHTML')

@rt
async def download():
    csv_data = [",".join(map(str, tbl.columns_dict))]
    csv_data += [",".join(map(str, row.values())) for row in tbl()]
    headers = {'Content-Disposition': 'attachment; filename="data.csv"'}
    return Response("n".join(csv_data), media_type="text/csv", headers=headers)

@rt('/update')
def post(d:dict): return render_row(tbl.update(d))

@rt
def remove(id:int): tbl.delete(id)

@rt("/upload")
async def post(csv_file: UploadFile):
    global tbl
    if not csv_file.filename.endswith('.csv'): return "Please upload a CSV file"
    tbl = db.import_file('test', await csv_file.read(), pk='id')
    header = Tr(*map(Th, tbl.columns_dict))
    vals = [render_row(row) for row in tbl()]
    return Table(Thead(header), Tbody(*vals))

serve()

And here’s how Jeremy’s app looks:

Jeremy's version of the FastHTML app

Next.JS

To learn Next.js, I started with the React Foundations and Next.js tutorials. The latter teaches the basics of Next.js through bite-sized, hands-on lessons that build up to a dashboard app. With 16 chapters, learning and coding along can take a while. I recommend persisting till at least Chapter 12 on transforming data to gain a solid understanding.

To create the Next.js app template, I ran:

npx create-next-app@latest

Building the same app in Next.js requires considerably more code than the Python versions (code). Nonetheless, I found the organizational structure intuitive:

  • components: Reusable React components like table and upload/download buttons
  • api: Routes for the data table (GET, PUT, DELETE) and file upload/download
  • lib: Utility functions; in this case, there was a single function for SQLite
  • pages.tsx and layout.tsx: Page-specific and common user interface components

Next.js app structure

And here’s how the web app looks. The built-in Tailwind CSS integration makes the app look more polished compared to the barebones FastAPI and FastHTML apps.

Next.js web app

SvelteKit

To learn Svelte, I went through part of their tutorial that comes with an online interpreter. The tutorial has four parts: (i) Basic Svelte, (ii) Advanced Svelte, (iii) Basic SvelteKit, and (iv) Advanced SvelteKit. I completed the sections on basic Svelte and basic SvelteKit and jumped into building the app (code).

To create the SvelteKit app template, I ran the following:

npm create svelte@latest my-app

Like Next.js, building the app in SvelteKit leads to lot of directories and moving parts. At a high level, it’s organized as follows:

  • components: Reusable Svelte components such as data table and upload buttons
  • api.ts and db.ts: Functions for the API to fetch, update, and delete data as well as query and run updates on the SQLite database
  • routes: Routes for table (GET), rows (PUT, DELETE), and upload/download
  • +page.svelte: Main page of the application
  • app.html: Entry point and main HTML file

SvelteKit app structure

Here’s how the app looks. One slight deviation: I played with combining the “choose file” and “upload” functionality into a single button, thus removing the “Upload CSV” button.

SvelteKit web app

FastAPI + Svelte

I also took a stab at building an app that had FastAPI as backend and Svelte for frontend (code). All backend functionality and APIs resided in main.py while frontend UI and API interactions were handled by +page.svelte and api.ts respectively. To run the app, I had to start both the FastAPI server and the Svelte development server simultaneously.

FastAPI + Svelte app structure

And here’s what the web app looks like. (I reverted the upload functionality to match the original FastAPI app that had a separate “Upload CSV” button.)

FastAPI + Svelte web app

The main challenge was coordinating communication between both servers during development. In a production setting, the Svelte app would be compiled and served statically with API requests sent to the FastAPI backend.

• • •

Aside: How will coding assistants influence builders?

This exercise got me thinking about how coding assistants, powered by LLMs, could influence the choices we make as builders. For example, would LLMs be as effective with newer or niche frameworks such as Svelte and FastHTML? While the tweet below may be an exaggeration, it raises a valid concern.

It brings me no pleasure to say this, but Svelte is dead because LLM base models are better at writing React. — Jess Martin

Given React’s popularity and longer history, it’s likely that most LLMs are pre and post-trained on more React code and Svelte code. The same applies to FastHTML. This could lead to coding assistants being more effective when working with established frameworks such as FastAPI, React, and Next.js.

As an anecdote, I had an easier time working with Cursor and Claude to build the app in FastAPI and Next.js compared to FastHTML and SvelteKit. This is understandable, given that FastHTML is barely a few months old (at the time of writing) and likely hasn’t made its way into the training data of most coding assistants yet.

To address this issue, Jeremy Howard, the creator of FastHTML, has made the extra effort to provide llms.txt and llms-ctx.txt that have been optimized for in-context learning. Similarly, Rich Harris, who works on Svelte at Vercel, has mentioned that they plan to publish more LLM-friendly documentation for Svelte. (Victor Dibia has also written about how coding assistants may influence developer habits and choices, and how we need to write docs for both humans and machines.)

Time will tell how effective these efforts will be in addressing the cold-start problem for newer frameworks in coding assistants.

• • •

This was a fun exercise to gain familiarity with FastHTML, Next.js, and SvelteKit. All the code can be found here; I’m a beginner with frontend so please excuse any bad practices! Personally, I’m looking forward to building more with TypeScript, which I haven’t used extensively since building applyingML.com years ago. What resources have you found useful in learning how to build with Next.js or Svelte? Please comment below or dm me!

If you found this useful, please cite this write-up as:

Yan, Ziyou. (Sep 2024). Building the Same App across Various Web Frameworks. eugeneyan.com.
https://eugeneyan.com/writing/web-frameworks/.

or

@article{yan2024frameworks,
  title   = {Building the Same App across Various Web Frameworks},
  author  = {Yan, Ziyou},
  journal = {eugeneyan.com},
  year    = {2024},
  month   = {Sep},
  url     = {https://eugeneyan.com/writing/web-frameworks/}
}

Share on:

Join 8,300+ readers getting updates on machine learning, RecSys, LLMs, and engineering.



Source link
lol

By stp2y

Leave a Reply

Your email address will not be published. Required fields are marked *

No widgets found. Go to Widget page and add the widget in Offcanvas Sidebar Widget Area.