Forms
File Upload Forms
Handle form submissions with file uploads using ps.Form and ps.FormData.
The problem
You need to accept file uploads from users, validate them, and process the contents server-side.
Solution
Use ps.Form with file inputs. Your handler receives UploadFile objects for any file fields.
import pulse as ps
from fastapi import UploadFile
class UploadState(ps.State):
filename: str | None = None
file_size: int | None = None
error: str | None = None
async def handle_upload(self, data: ps.FormData):
"""Process the uploaded file."""
file = data.get("document")
if not isinstance(file, UploadFile):
self.error = "No file uploaded"
return
# Read file content
content = await file.read()
# Access file metadata
self.filename = file.filename
self.file_size = len(content)
self.error = None
# Save to disk or process as needed
# await save_to_storage(file.filename, content)
@ps.component
def FileUploadForm():
with ps.init():
state = UploadState()
return ps.div(
ps.h2("Upload a Document"),
ps.Form(key="upload", onSubmit=state.handle_upload)[
ps.label("Select file", htmlFor="doc"),
ps.input(
id="doc",
name="document",
type="file",
accept=".pdf,.doc,.docx",
required=True,
),
ps.button("Upload", type="submit"),
],
# Show result
ps.div(
ps.p(f"Uploaded: {state.filename} ({state.file_size} bytes)")
if state.filename else None,
ps.p(state.error, style={"color": "red"})
if state.error else None,
),
)Multiple file uploads
For multiple files, add multiple=True to the input. The handler receives a list:
class MultiUploadState(ps.State):
files: list[dict] = []
async def handle_upload(self, data: ps.FormData):
attachments = data.get("files")
# Handle both single and multiple files
if isinstance(attachments, list):
file_list = attachments
elif isinstance(attachments, UploadFile):
file_list = [attachments]
else:
file_list = []
self.files = []
for file in file_list:
content = await file.read()
self.files.append({
"name": file.filename,
"size": len(content),
"type": file.content_type,
})
@ps.component
def MultiFileForm():
with ps.init():
state = MultiUploadState()
return ps.Form(key="multi-upload", onSubmit=state.handle_upload)[
ps.input(name="files", type="file", multiple=True),
ps.button("Upload All", type="submit"),
]Tracking submission state
Use ps.ManualForm for more control, including access to is_submitting:
@ps.component
def FormWithProgress():
with ps.init():
state = UploadState()
manual_form = ps.setup(lambda: ps.ManualForm(state.handle_upload))
return ps.div(
ps.p("Uploading...") if manual_form.is_submitting else None,
ps.form(**manual_form.props())[
ps.input(name="document", type="file", required=True),
ps.button(
"Upload",
type="submit",
disabled=manual_form.is_submitting,
),
],
)How it works
ps.Formrequires a uniquekeyto identify the form- The
onSubmithandler receivesps.FormData, a dict of field names to values - Text fields are strings; file inputs are
UploadFileobjects UploadFileprovidesfilename,content_type,size, andread()method- Forms are automatically bound to the user session for security