Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Nova Date Range Laravel Package

kpolicar/nova-date-range

kpolicar/nova-date-range is a Laravel Nova package that adds a date range field and picker for selecting start and end dates in resource forms and filters. It simplifies date range input in Nova admin panels with a clean, configurable UI for working with date-based records.

View on GitHub
Deep Wiki
Context7

Getting Started

  1. Check compatibility before you install. Open the package's composer.json on GitHub and compare its Nova/Laravel constraints against your composer.json. With ~26 stars and a low opportunity score, this is almost certainly a Vue 2-era component, so it is a safe bet for Nova 2/3 and a coin-flip for Nova 4/5. Run composer require kpolicar/nova-date-range and pin it ("kpolicar/nova-date-range": "1.*") rather than floating *.
  2. Read the field class first, not the README. The README is short; src/DateRange.php tells you the real API surface — the column-configuration methods (->startColumn() / ->endColumn() in most builds, sometimes positional args to make()), the picker format default, and which resolveUsing/fillUsing hooks are already overridden.
  3. Add the two backing columns. The field does not store one value; it writes a start and an end. Migrate both:
    $table->dateTime('starts_at')->nullable()->after('id');
    $table->dateTime('ends_at')->nullable()->after('starts_at');
    
  4. Wire up your first resource field:
    use Kpolicar\DateRange\DateRange;
    
    DateRange::make('Active period')
        ->startColumn('starts_at')
        ->endColumn('ends_at')
        ->format('Y-m-d'),   // verify this method name in the source
    
  5. Verify with a round trip. Create a record in the Nova UI, then dd($model->fresh()->only(['starts_at', 'ends_at'])). If you get two populated columns, the field is working and you can move on to index/filter concerns.

The natural first use case is an event, campaign, or subscription "active window" on a resource form — anywhere you currently have two sibling Date fields that users expect to pick on one calendar.

Implementation Patterns

Form-only field + computed index column. Because the field resolves to two attributes rather than one, Nova's index table has nothing to render under its name. The reliable pattern is:

DateRange::make('Active period')
    ->startColumn('starts_at')
    ->endColumn('ends_at')
    ->onlyOnForms(),

Text::make('Active period', fn () => $this->starts_at && $this->ends_at
        ? $this->starts_at->toDateString() . ' → ' . $this->ends_at->toDateString()
        : '—')
    ->onlyOnIndex(),

Reuse via a base field. If every resource needs the same format and validation, subclass it once and keep ->format(), ->rules(), and ->help() in one place instead of repeating them across ten resources.

Pair it with a custom filter, not the built-in ones. whereBetween on a single column does not express "ranges that overlap this window." Write a NovaFilter that takes a range and queries the overlap:

$query->where('starts_at', '<=', $rangeEnd)
      ->where('ends_at', '>=', $rangeStart);

Single JSON column variant. If your schema prefers one column, override fillUsing/resolveUsing so the field serialises to {start, end} and you cast the attribute with 'period' => 'array' or a custom cast. Keep the write path in the field, not the model, so the picker stays a drop-in.

Use it anywhere a field goes. Actions and tool modals accept Nova fields, so the same component works for "bulk-shift the date range" style operations. Keep the action's handle() aware that $fields->starts_at and $fields->ends_at arrive as separate values, not one object.

Gotchas and Tips

  • The payload shape bites validation. The browser submits something like {active_period: {start: ..., end: ...}} while your rules target starts_at/ends_at. ->rules('required|date') may attach to a key that never arrives. Prefer explicit per-column validation ('required_with:ends_at') or, on Nova 4+, ->rulesFor() — and confirm by dumping $request->all() on a failed submit.
  • Sorting silently does nothing. ->sortable() maps to one attribute; there is no column named after the field, so the request is harmless but ineffective. Sort on starts_at explicitly or drop the affordance.
  • Timezone drift. Nova's built-in date fields convert between UTC storage and the user's timezone; a third-party picker usually does not. If your app stores UTC, either normalise in fillUsing or accept the drift deliberately. Test with a non-UTC config('app.timezone').
  • Clearing one side. Users clear the end date and leave the start. Guard against half-populated ranges or you'll ship rows that fail your own overlap query later.
  • Format mismatch with MySQL. If the picker submits d/m/Y and the column is DATETIME, you'll get 0000-00-00 or an exception. Set the field format to something the DB accepts, or cast on the model and let the mutator parse.
  • Debugging after a Nova upgrade. Blank field or an empty container almost always means the Vue asset bundle didn't rebuild or is Vue-2-shaped. Check the browser console first, then php artisan vendor:publish --force for the package's asset tag if one exists, then nova:publish.
  • Treat the source as the extension point. For a package this small, ->withMeta([...]) to pass picker options through, subclassing for defaults, and a local fork are all cheaper than filing issues. Copy src/ into your own namespace if you need Vue 3 compatibility — at this star count, upstream fixes are unlikely to arrive on your timeline.
  • Pin and monitor. Lock the version in composer.lock, and if you're on Nova 4/5, budget a day to port the component rather than assuming it will work out of the box.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
coleus/frame
thtg88/journalism
filagora/filagora-ai-forums
rlacerda83/query-parser
socialdept/atp-orm
cboxdk/laravel-assistant-client
socialdept/atp-resolver
socialdept/tenant-domains
tautvydasr/blade-class-names
socialdept/color-scales
maherelgamil/controller
coleus/widgets
coleus/table
coleus/users
coleus/music
coleus/health
coleus/calendar
coleus/notes
coleus/support
coleus/settings