Cursor rule
.cursor/rules/fasthtml.mdc[object Object]
Cursor rules
Quality
81/100
Scores the file, not the repository.Length
1,195 words
30 headings · 13 code blocksRepository
69
— · pushed 363 days agoLast changed
3 days ago
First indexed 3 days ago.1234567# Project Structure89The main file for the project is [main_sqlite.py](mdc:fasthtml_app/main_sqlite.py) It uses MonsterUI for styling. MonsterUI is a python first UI component library that primarily leverages FrankenUI and Tailwind, but also includes headers and functionality form DaisyUI, Katex, HighlightJS, and others.1011You can run the server with the command `python app/main_sqlite.py`, which will start the server on http://localhost:5001.1213# Tech Stack1415- FastHTML is the web application framework. It is built on top of starlette and uvicorn.16- MonsterUI is a UI component library designed to work well with FastHTML17- Fastlite is a sqlite library that is a small wrapper on top of sqlite-utils1819# Key documentation files:2021These are relevant documents that should be referenced while building a FastHTML App.2223- [fasthtml.mdc](mdc:.cursor/rules/fasthtml.mdc): Minimal HTMX integration exaples to show how HTMX can be used with fasthtml24- [db.md](mdc:ref_docs/db.md): MiniDataAPI Spec for database operations25- [monsterui_api.md](mdc:ref_docs/monsterui_api.md).md : MonsterUI full API list and idiomatic examples for UI components2627# FastHTML examples2829Reference these examples when constructing new FastHTML applications.3031- [annotation.md](mdc:ref_docs/annotation.md): A siple example of a annotation app to evaluate search results.32- Adv_app: Example FastHTML To-Do-List application that demonstrates core FastHTML features including authentication, HTMX integration, and database operations. It allows users to create, edit, delete, and reorder todos with markdown support, using SQLite for storage.3334# FastHTML Rules3536- Use `serve()` directly - no need for uvicorn or separate ASGI server37- Not compatible with FastAPI syntax - FastHTML is for HTML-first apps, not API services38- Define routes with decorators and return HTML components or strings39- Use python FastTags (ie `Div`, `P`) instead of raw HTML where possible40- Use HTMX for interactive features, vanilla JS where needed. No React/Vue/Svelte4142# UI Design Elements with MonsterUI4344- Use defaults as much as possible, for example `Container` in monsterui already has defaults for margins45- Use `*T` for button styling consistency, for example `ButtonT.destructive` for a red delete button or `ButtonT.primary` for a CTA button46- Use `Label*` functions for forms as much as possible (e.g. `LabelInput`, `LabelRange`) which creates and links both the `FormLabel` and user input appropriately to avoid boiler plate4748## Basic Complete App Example4950```python51from fasthtml.common import *52from monsterui.all import *5354app, rt = fast_app(hdrs=Theme.blue.headers()) # Use MonsterUI blue theme5556@rt57def index():58 socials = (('github','https://github.com/AnswerDotAI/MonsterUI'),59 ('twitter','https://twitter.com/isaac_flath/'),60 ('linkedin','https://www.linkedin.com/in/isaacflath/'))61 return Titled("Your First App",62 Card(63 P("Your first MonsterUI app", cls=TextPresets.muted_sm),64 # LabelInput, DivLAigned, and UkIconLink are non-semantic MonsterUI FT Components,65 LabelInput('Email', type='email', required=True),66 footer=DivLAligned(*[UkIconLink(icon,href=url) for icon,url in socials])))67```6869## Card and Flex Layout Components Example7071```python72def TeamCard(name, role, location="Remote"):73 icons = ("mail", "linkedin", "github")74 return Card(75 DivLAligned(76 DiceBearAvatar(name, h=24, w=24),77 Div(H3(name), P(role))),78 footer=DivFullySpaced(79 DivHStacked(UkIcon("map-pin", height=16), P(location)),80 DivHStacked(*(UkIconLink(icon, height=16) for icon in icons))))81```8283## Forms and User Inputs Example8485```python86def MonsterForm():87 relationship = ["Parent",'Sibling', "Friend"]88 return Div(89 DivCentered(90 H3("Emergency Contact Form"),91 P("Please fill out the form completely", cls=TextPresets.muted_sm)),92 Form(93 Grid(LabelInput("Name",id='name'),LabelInput("Email", id='email')),94 H3("Relationship to patient"),95 Grid(*[LabelCheckboxX(o) for o in relationship], cols=4, cls='space-y-3'),96 DivCentered(Button("Submit Form", cls=ButtonT.primary))),97 cls='space-y-4')98```99100## Markdown Text Styling Example101102```python103render_md("""104# My Document105106> Important note here107108+ List item with **bold**109+ Another with `code`110111```python112def hello():113 print("world")114```115""")116```117118## Semantic Text Styling Example119120```python121def SemanticText():122 return Card(123 H1("MonsterUI's Semantic Text"),124 P(125 Strong("MonsterUI"), " brings the power of semantic HTML to life with ",126 Em("beautiful styling"), " and ", Mark("zero configuration"), "."),127 Blockquote(128 P("Write semantic HTML in pure Python, get modern styling for free."),129 Cite("MonsterUI Team")),130 footer=Small("Released February 2025"),)131```132133# Data Storage134135- `fastlite` (SQLite) included and preferred.136- `sqlite-utils` is also a good option and sqlite-utils is compatible with fastlite137138## Creating Tables139140```python141class Book: isbn: str; title: str; pages: int; userid: int142# The transform arg instructs fastlite to change the db schema when fields change.143# Create only creates a table if the table doesn't exist.144books = db.create(Book, pk='isbn', transform=True)145146class User: id: int; name: str; active: bool = True147# If no pk is provided, id is used as the primary key.148users = db.create(User, transform=True)149```150151## Crud operations152153```python154# creating records155user = users.insert(name='Alex',active=False)156# List all records157users()158# Limit, offset, and order results:159users(order_by='name', limit=2, offset=1)160# Filter on the results161users(where="name='Alex'")162# Placeholder for avoiding injection attacks163users("name=?", where_args=('Alex',))164# fetch by primary key165users[user.id]166# Record exists check based on primary key1671 in users168# Updates169user.name='Lauren'170user.active=True171users.update(user)172# Deleting records173users.delete(user.id)174```175176# Interactivity177178JS can be added via a `Script` tag. Small scripts should be inline, where larger ones should use a seperate `.js` file.179180```python181def index():182 data = {'somedata':'fill me in…'}183 # `Titled` returns a title tag and an h1 tag with the 1st param, along with all other params as HTML in a `Main` parent element.184 return Titled("Chart Demo", Div(id="myDiv"), Script(f"var data = {data}; Plotly.newPlot('myDiv', data);"))185```186187However, it is preferred to use HTMX. See a few examples of how HTMX with FastHTMl and MonsterUI works.188189## File Upload Example190191```python192@rt193def index():194 inp = Card(195 H3("Drag and drop images here"),196 # HTMX for uploading multiple images197 Input(type="file",name="images", multiple=True, required=True,198 # Call the upload route on change199 hx_post=upload, hx_target="#image-list", hx_swap="afterbegin", hx_trigger="change",200 # encoding for multipart201 hx_encoding="multipart/form-data",accept="image/*"))202203 return DivCentered(inp, H3("👇 Uploaded images 👇"), Div(id="image-list"))204205async def ImageCard(image):206 contents = await image.read()207 # Create a base64 string208 img_data = f"data:{image.content_type};base64,{b64encode(contents).decode()}"209 # Create a card with the image210 return Card(H4(image.filename), Img(src=img_data, alt=image.filename))211212@rt213async def upload(images: list[UploadFile]):214 # Create a grid filled with 1 image card per image215 return Grid(*[await ImageCard(image) for image in images])216```217218## Cascading DropDown Example219220```python221def mk_opts(nm, cs):222 return (223 ft.Option(f'-- select {nm} --', disabled='', selected='', value=''),224 *map(ft.Option, cs))225226@rt227def get_lessons(chapter: str):228 return ft.Select(*mk_opts('lesson', lessons[chapter]), name='lesson')229230@rt231def index():232 chapter_dropdown = ft.Select(233 *mk_opts('chapter', chapters),234 name='chapter',235 hx_get=get_lessons, hx_target='#lessons',236 label='Chapter:')237238 return Container(239 DivLAligned(FormLabel("Chapter:", for_="chapter"),chapter_dropdown),240 DivLAligned(241 FormLabel("Lesson:", for_="lesson"),242 Div(id='lessons')),243 cls='space-y-4')244```245246247248Session data in Websockets249Session data is shared between standard HTTP routes and Websockets. This means you can access, for example, logged in user ID inside websocket handler:250```251252from fasthtml.common import *253254app = FastHTML(exts='ws')255rt = app.route256257@rt('/login')258def get(session):259 session["person"] = "Bob"260 return "ok"261262@app.ws('/ws')263async def ws(msg:str, send, session):264 await send(Div(f'Hello {session.get("person")}' + msg, id='notifications'))265266serve()267```268269270```271 Real-Time Chat App272Let’s put our new websocket knowledge to use by building a simple chat app. We will create a chat app where multiple users can send and receive messages in real time.273274Let’s start by defining the app and the home page:275276from fasthtml.common import *277278app = FastHTML(exts='ws')279rt = app.route280281msgs = []282@rt('/')283def home(): return Div(284 Div(Ul(*[Li(m) for m in msgs], id='msg-list')),285 Form(Input(id='msg'), id='form', ws_send=True),286 hx_ext='ws', ws_connect='/ws')287288Now, let’s handle the websocket connection. We’ll add a new route for this along with an on_conn and on_disconn function to keep track of the users currently connected to the websocket. Finally, we will handle the logic for sending messages to all connected users.289290users = {}291def on_conn(ws, send): users[str(id(ws))] = send292def on_disconn(ws): users.pop(str(id(ws)), None)293294@app.ws('/ws', conn=on_conn, disconn=on_disconn)295async def ws(msg:str):296 msgs.append(msg)297 # Use associated `send` function to send message to each user298 for u in users.values(): await u(Ul(*[Li(m) for m in msgs], id='msg-list'))299300serve()301```302303304305306
Also in ai-evals-course/isaac-fasthtml-workshop
Diff this repo’s formatsOne repository carrying more than one format is the comparison this product exists for: does anyone actually write different content in each file, or is one a copy of the other?
| Repository | Format | Stack | Covers | Score | Changed |
|---|---|---|---|---|---|
| ai-evals-course/isaac-fasthtml-workshop.cursor/rules/fastapi-react-backend-rules.mdc · 69 | Cursor rules | lint-formatstyledependenciesdo-not | 40/100 | 3 days ago | |
| ai-evals-course/isaac-fasthtml-workshop.cursor/rules/fastapi-react-frontend-rules.mdc · 69 | Cursor rules | lint-formatstyledependenciesdo-not | 54/100 | 3 days ago | |
| ai-evals-course/isaac-fasthtml-workshop.cursor/rules/htmx.mdc · 69 | Cursor rules | database | 54/100 | 3 days ago | |
| ai-evals-course/isaac-fasthtml-workshop.cursor/rules/ui.mdc · 69 | Cursor rules | no sections | 16/100 | 3 days ago |
Similar configs
Same format, overlapping stack, ranked by quality.
| Repository | Format | Stack | Covers | Score | Changed |
|---|---|---|---|---|---|
| TechSquidTV/Hermes.cursor/rules/10-hermes-api.mdc · 45 | Cursor rules | testlint-formatstylearch+5 | 100/100 | 3 days ago | |
| dodgecfr/combatfilms-webapp.cursor/rules/frontend.mdc · 0 | Cursor rules | setuptestlint-formatstyle+7 | 99/100 | 3 days ago | |
| deifos/clipmira-subtitles.cursor/rules/frontend.mdc · 1 | Cursor rules | setuptestlint-formatstyle+7 | 99/100 | 3 days ago | |
| markstev/mark-starter.cursor/rules/frontend.mdc · 0 | Cursor rules | setuptestlint-formatstyle+6 | 99/100 | 3 days ago | |
| Allymahmoud/case-intake-platform.cursor/rules/frontend.mdc · 0 | Cursor rules | setuptestlint-formatstyle+7 | 99/100 | 3 days ago | |
| langflow-ai/langflow.cursor/rules/docs_development.mdc · 153k | Cursor rules | setupbuildtestlint-format+7 | 97/100 | 3 days ago | |
| TechSquidTV/Hermes.cursor/rules/20-hermes-api-tests.mdc · 45 | Cursor rules | teststyletesting-strategysecurity+3 | 97/100 | 3 days ago | |
| skillrecordings/egghead-next.cursor/rules/gh-task-plan.mdc · 1.4k | Cursor rules | teststylearchtypes+2 | 96/100 | 3 days ago |
