Form submission tracking and analytics in Laravel — without third-party tools | FilaForms                                 [ ![Filaforms Logo](https://filaforms.app/logo.svg)FilaForms

 ](https://filaforms.app)  [ Features ](https://filaforms.app#features) [ Pricing ](https://filaforms.app#pricing) [ Blog ](https://filaforms.app/blog) [ Documentation ](https://docs.filaforms.app)  [ Try Demo ](https://filaforms.app/login) [ Get Started ](https://filaforms.app#pricing)

 [ Features ](https://filaforms.app#features) [ Pricing ](https://filaforms.app#pricing) [ Blog ](https://filaforms.app/blog) [ Documentation ](https://docs.filaforms.app) [ Try Demo ](https://filaforms.app/login) [ Get Started ](https://filaforms.app#pricing)

   ![FilaForms](https://filaforms.app/logo.svg) FilaForms

 TutorialsForm submission tracking and analytics in Laravel — without third-party tools
=============================================================================

 filaforms.app/blog

  [    Back to blog ](https://filaforms.app/blog) [ Tutorials ](https://filaforms.app/blog/category/tutorials)

Form submission tracking and analytics in Laravel — without third-party tools
=============================================================================

 Manuk Minasyan ·  March 20, 2025  · 7 min read

 What's the completion rate on your [contact form](https://filaforms.app/blog/building-a-contact-form-in-laravel-with-filament-step-by-step)?

If you don't know, you're in good company. Most Laravel developers ship forms without any form submission tracking and never measure whether those forms actually work. The form exists, submissions come in, everyone assumes it's fine. (If you're still [building forms from scratch](https://filaforms.app/blog/filaforms-vs-building-forms-from-scratch-in-laravel), adding analytics is even further down the priority list.)

But "submissions come in" doesn't tell you much. How many people loaded the form and left without filling it out? How many started typing and gave up halfway? Is the form too long? Is a specific field causing friction? You have no idea.

You can fix this without Google Analytics or any third-party tool.

What to track
-------------

Form analytics boil down to four events:

**View** — someone loaded the page with the form on it. This is your baseline. If 1,000 people view the form and 50 submit it, you have a 5% submission rate. That number tells you whether the problem is the form or the traffic.

**Start** — someone interacted with the first field. They clicked or tapped into the form and began filling it out. The gap between views and starts tells you whether people are interested but not engaging. If views are high and starts are low, the form might be below the fold, or the page copy isn't convincing enough.

**Submit** — someone completed and submitted the form. You're probably already tracking this (because you store submissions). But without views and starts, you can't calculate meaningful rates.

**Completion rate** — submissions divided by starts, as a percentage. This is the number that actually matters. Industry benchmarks vary, but a contact form under 50% completion rate probably has a problem. A multi-step application form at 30% might be normal.

There's a fifth metric worth tracking: **average completion time**. If your contact form takes people 8 minutes to fill out, something is wrong. If a 10-field application form takes 90 seconds, people are probably rushing through it and your data quality suffers.

Building it yourself
--------------------

If you want to roll your own tracking, here's the approach.

### The events table

```php
Schema::create('form_events', function (Blueprint $table) {
    $table->id();
    $table->foreignId('form_id')->constrained()->cascadeOnDelete();
    $table->string('event_type'); // VIEW, START, SUBMIT
    $table->string('session_hash', 64); // anonymized visitor ID
    $table->json('metadata')->nullable();
    $table->timestamps();
});

```

### Anonymizing visitors

You need to distinguish between visitors without storing personal data. Hash a combination of the session ID and date:

```php
use Illuminate\Support\Facades\Hash;
function generateSessionHash(): string
{
$raw = request()->session()->getId() . now()->toDateString();
return hash('sha256', $raw);
}

```

This gives you a unique-per-day identifier that can't be reversed back to a real session. The date component means the same person visiting tomorrow gets a different hash, so you're not building a tracking profile.

### Tracking views

Fire a VIEW event when the form page loads. Deduplicate by session hash so refreshes don't inflate the count.

```php
public function trackView(Form $form): void
{
    $hash = generateSessionHash();
FormEvent::firstOrCreate([
    'form_id' =&gt; $form-&gt;id,
    'event_type' =&gt; 'VIEW',
    'session_hash' =&gt; $hash,
]);

}

```

### Tracking starts

Fire a START event on the first field interaction. You'll need a bit of JavaScript for this:

```javascript
// On your form page
let formStarted = false;
document.querySelectorAll('form input, form textarea, form select')
.forEach(field => {
field.addEventListener('focus', () => {
if (formStarted) return;
formStarted = true;
        fetch(`/forms/${formId}/track`, {
            method: 'POST',
            headers: {
                'Content-Type': 'application/json',
                'X-CSRF-TOKEN': document.querySelector('meta[name=&quot;csrf-token&quot;]').content,
            },
            body: JSON.stringify({ event: 'START' }),
        });
    });
});

```

### Tracking submissions

Fire a SUBMIT event when the form is successfully processed. This one's easy — add it to your existing submission handler:

```php
public function submit(): void
{
    $data = $this->form->getState();
// Store submission...

FormEvent::create([
    'form_id' =&gt; $this-&gt;formModel-&gt;id,
    'event_type' =&gt; 'SUBMIT',
    'session_hash' =&gt; generateSessionHash(),
    'metadata' =&gt; [
        'started_at' =&gt; session(&quot;form_{$this-&gt;formModel-&gt;id}_started_at&quot;),
        'submitted_at' =&gt; now()-&gt;toISOString(),
    ],
]);

}

```

### Calculating metrics

```php
public function getMetrics(Form $form, Carbon $from, Carbon $to): array
{
    $events = FormEvent::where('form_id', $form->id)
        ->whereBetween('created_at', [$from, $to]);
$views = (clone $events)-&gt;where('event_type', 'VIEW')-&gt;count();
$starts = (clone $events)-&gt;where('event_type', 'START')-&gt;count();
$submissions = (clone $events)-&gt;where('event_type', 'SUBMIT')-&gt;count();

return [
    'views' =&gt; $views,
    'starts' =&gt; $starts,
    'submissions' =&gt; $submissions,
    'view_to_start_rate' =&gt; $views &gt; 0
        ? round(($starts / $views) * 100, 1)
        : 0,
    'completion_rate' =&gt; $starts &gt; 0
        ? round(($submissions / $starts) * 100, 1)
        : 0,
];

}

```

### Building a dashboard

You could build a Filament widget or page to display these metrics. A few stat cards, maybe a line chart showing submissions over time. That's another half-day of work on top of the tracking implementation.

### Total effort

To build proper form analytics from scratch, you're looking at:

- Events table and model
- Session hashing logic
- View tracking (server-side)
- Start tracking (JavaScript + API endpoint)
- Submit tracking (server-side)
- Metrics calculation service
- Admin dashboard to display it all
- Testing to make sure deduplication works

Realistically, that's a full day. Maybe more if you want charts and date range filtering.

The easier path
---------------

FilaForms includes all of this out of the box.

Every form automatically tracks views, starts, and submissions using an event-based system with SHA-256 session fingerprinting. No configuration needed — it starts tracking the moment you publish a form.

In the admin panel, each form shows:

- **Form views** — unique sessions that loaded the form
- **Form starts** — sessions where someone interacted with a field
- **Submissions** — completed forms
- **Completion rate** — (submissions / starts) × 100
- **Average completion time** — from first interaction to submission

No JavaScript to write, no API endpoints to build, no dashboard to design. It's there when you open the form in the admin.

The tracking is privacy-compliant by design. Session fingerprints use SHA-256, and no personally identifiable information is stored in the events table. Submission data lives in the submissions table; analytics data stays fully anonymized.

What the data tells you
-----------------------

Once you have form analytics, you start noticing things.

**Low view-to-start rate (under 30%)** — people see the form but don't engage. Maybe it's below the fold, or the page copy isn't convincing, or the form looks too long at first glance. Try moving it higher on the page or reducing visible fields.

**Low completion rate (under 40% for a contact form)** — people start filling it out but give up. A specific field might be confusing, or you have too many required fields. On multi-step forms, check which step people abandon.

**High completion time (over 5 minutes for a simple form)** — something is causing friction. An unclear field label, slow file upload, or confusing validation errors.

**Completion rate drops after a change** — you added a field or changed the layout and completions went down. Now you have data to revert instead of guessing.

None of these insights are possible if you only count submissions. The funnel (views → starts → submissions) is what makes the data useful.

Stop guessing about your forms
------------------------------

Most of us ship features with error tracking and run campaigns with conversion metrics, but ship forms with zero visibility. That's a gap worth closing.

Whether you build tracking yourself or use a tool that includes it, measure your forms. You'll find problems you didn't know existed.

**Resources:**

- [FilaForms documentation](https://docs.filaforms.app)
- [FilaForms analytics docs](https://docs.filaforms.app/core-features/analytics)

 Related posts
-------------

 [  Tutorials   Apr 30, 2026

 File Uploads in Filament Forms: Storage, Validation, and Security
-------------------------------------------------------------------

File uploads are where most form builders cut corners. They'll let you add a file field and call it done, leaving you to figure out storage and validation

 ](https://filaforms.app/blog/file-uploads-in-filament-forms-storage-validation-and-security) [  Tutorials   Mar 30, 2025

 Conditional logic in Laravel forms: when fields should show (and hide)
------------------------------------------------------------------------

How to build forms where fields appear based on previous answers. Covers Filament's reactive forms, Livewire approaches, and common patterns.

 ](https://filaforms.app/blog/conditional-logic-in-laravel-forms-when-fields-should-show-and-hide) [  Tutorials   Mar 27, 2025

 Multi-step forms in Filament: a complete guide
------------------------------------------------

How to build multi-step forms in Laravel with Filament — wizard components, progress tracking, conditional steps, and where most implementations go wrong.

 ](https://filaforms.app/blog/multi-step-forms-in-filament-a-complete-guide)

    ![FilaForms Logo](/logo.svg) FilaForms

 Laravel form infrastructure for Filament. Stop rebuilding forms on every project.

 ### Product

 [ Features ](https://filaforms.app#features) [ Documentation ](https://docs.filaforms.app) [ Blog ](https://filaforms.app/blog) [ Pricing ](https://filaforms.app#pricing) [ Contact ](mailto:hello@filaforms.app)

 ### Legal

 [ Terms of Service ](https://filaforms.app/terms-of-service) [ Privacy Policy ](https://filaforms.app/privacy-policy)

  © 2025-2026 FilaForms. All rights reserved.

 [    ](mailto:hello@filaforms.app) [    ](https://x.com/MinasyanManuk)
