Files
personal-portfolio/docs/project-lessons-learned/sprint-10-formspree-dash-integration.md
l3ocho a5d6866d63
Some checks failed
CI / lint-and-test (push) Has been cancelled
feat(contact): implement Formspree contact form submission
- Enable contact form fields with component IDs
- Add callback for Formspree POST with JSON/AJAX
- Include honeypot spam protection (_gotcha field)
- Handle validation, loading, success/error states
- Clear form on successful submission
- Add lessons learned documentation

Closes #92, #93, #94

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-01 15:00:04 -05:00

2.6 KiB

Sprint 10 - Formspree Integration with Dash Callbacks

Context

Implementing a contact form on a Dash portfolio site that submits to Formspree, a third-party form handling service.

Insights

Formspree AJAX Submission

Formspree supports AJAX submissions (no page redirect) when you:

  1. POST with Content-Type: application/json
  2. Include Accept: application/json header
  3. Send form data as JSON body

This returns a JSON response instead of redirecting to a thank-you page, which is ideal for single-page Dash applications.

Dash Multi-Output Callbacks for Forms

When handling form submission with validation and feedback, use a multi-output callback pattern:

@callback(
    Output("feedback-container", "children"),  # Success/error alert
    Output("submit-button", "loading"),        # Button loading state
    Output("field-1", "value"),                # Clear on success
    Output("field-2", "value"),                # Clear on success
    Output("field-1", "error"),                # Field-level errors
    Output("field-2", "error"),                # Field-level errors
    Input("submit-button", "n_clicks"),
    State("field-1", "value"),
    State("field-2", "value"),
    prevent_initial_call=True,
)

Use no_update for outputs you don't want to change (e.g., keep form values on validation error, only clear on success).

Honeypot Spam Protection

Simple and effective bot protection without CAPTCHA:

  1. Add a hidden text input field (CSS: position: absolute; left: -9999px)
  2. Set tabIndex=-1 and autoComplete="off" to prevent accidental filling
  3. In callback, check if honeypot has value - if yes, it's a bot
  4. For bots: return fake success (don't reveal detection)
  5. For humans: proceed with real submission

Formspree also accepts _gotcha as a honeypot field name in the JSON payload.

Code Pattern

# Honeypot check - bots fill hidden fields
if honeypot_value:
    # Fake success - don't let bots know they were caught
    return (_create_success_alert(), False, "", "", None, None)

# Real submission for humans
response = requests.post(
    FORMSPREE_ENDPOINT,
    json=form_data,
    headers={"Accept": "application/json", "Content-Type": "application/json"},
    timeout=10,
)

Prevention/Best Practices

  • Always use timeout parameter with requests.post() to avoid hanging
  • Wrap external API calls in try/except for network errors
  • Return user-friendly error messages, not technical details
  • Use DMC's required=True and error props for form validation feedback

Tags

formspree, dash, callbacks, forms, spam-protection, honeypot, ajax, python, requests, validation