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

Address Autocomplete Laravel Package

naif/address_autocomplete

Add address autocomplete to your Laravel forms with this package. As users type, it suggests matching addresses and can auto-fill fields like street, city, and postal code, making checkout and address entry faster and less error-prone.

View on GitHub
Deep Wiki
Context7

Getting Started

Install and register the field

composer require naif/address_autocomplete
php artisan vendor:publish --provider="Naif\AddressAutocomplete\FieldServiceProvider"

The package ships a Nova field, a config file, and a Vue component. After publishing, open config/address_autocomplete.php and drop in your Google Places key — most versions read an env var such as GOOGLE_PLACES_API_KEY or NOVA_ADDRESS_AUTOCOMPLETE_KEY, so set that in .env and config/address_autocomplete.php will pick it up. Remember php artisan config:clear after editing the env.

First use case: one address field on a resource

In the Nova resource you want to enrich (e.g. app/Nova/Customer.php):

use Naif\AddressAutocomplete\AddressAutocomplete;

public function fields(Request $request)
{
    return [
        ID::make()->sortable(),
        Text::make('Name'),
        AddressAutocomplete::make('Address'),
    ];
}

Make sure the underlying address column exists on the model and is fillable. Add Address to $casts if you plan to store structured data (see below), then load /nova/resources/customers/1/edit and start typing — the field should attach Google Places autocomplete to the input and write the selected formatted address into the column on save.

Where to look first

  1. config/address_autocomplete.php — the key and any default options (country/type restrictions, script inclusion).
  2. resources/js/components/Field.vue (or similarly named) inside the package — this is where the Places widget is mounted; almost every customisation ends up here or in a subclass.
  3. The field's PHP class in src/ — check which withMeta() keys are exposed as fluent setters.

If the dropdown never appears, open DevTools → Network, filter for maps.googleapis.com, and confirm the script loaded and the key is being serialised into the field meta before you debug anything else.

Implementation Patterns

Plain address string (simplest)

The package's default behaviour is a single Text-style column that stores the formatted address. Good for line-of-business apps where you only need "what the user selected" for display or mailing labels.

Store the full place, not just a string

Autocomplete returns a rich PlaceResult. The common pattern is to keep the formatted address in address and cast a companion JSON column:

// migration
$table->string('address')->nullable();
$table->json('address_components')->nullable();
$table->decimal('latitude', 10, 7)->nullable();
$table->decimal('longitude', 10, 7)->nullable();
// model
protected $casts = ['address_components' => 'array'];

Then in the field's withMeta()/component, listen to the place_changed event and write geometry.location and address_components into hidden fields. This is the highest-leverage pattern: it turns the widget from a convenience into structured data you can later geo-query or validate.

Mapping components to first-class columns

Most teams don't want a JSON blob in the DB. Use an accessor or an AddressData value object to pull street_number, route, locality, postal_code, country out of address_components on save:

protected static function booted()
{
    static::saving(function ($model) {
        $parts = collect($model->address_components ?? [])
            ->keyBy(fn ($c) => $c['types'][0]);

        $model->city    = $parts['locality']['long_name'] ?? null;
        $model->zip     = $parts['postal_code']['long_name'] ?? null;
        $model->country = $parts['country']['short_name'] ?? null;
    });
}

Restrict to a country or address type

Use the field's fluent options (or config defaults) so you don't get PO boxes or airport names in a shipping form:

AddressAutocomplete::make('Shipping Address')
    ->countries(['us', 'ca'])
    ->types(['address']);

Keep the restriction in the field and in validation — the widget is client-side and can be bypassed.

Pairing with validation

Don't trust the string blindly. A pragmatic rule set:

'address' => ['required', 'string', 'max:255'],
'latitude' => ['nullable', 'numeric', 'between:-90,90'],
'longitude' => ['nullable', 'numeric', 'between:-180,180'],

If you require a verified place, store a flag set when place_changed fires and reject submissions without it. This catches users who type free text and never pick a suggestion.

Reuse across resources

Wrap the field in a small factory so every resource gets identical options:

class AddressField extends AddressAutocomplete
{
    public function __construct($name, $attribute = null, $resolveCallback = null)
    {
        parent::__construct($name, $attribute, $resolveCallback);
        $this->countries(['us'])->types(['address']);
    }
}

Then use AddressField::make('Address') everywhere — one place to change when Google's API drifts.

Detail and index views

The field is form-oriented. On detail/index, either use ->onlyOnForms() and render the stored string with a separate Text::make('Address')->exceptOnForms(), or format it as a Google Maps link:

Text::make('Address', function () {
    return sprintf(
        '<a href="https://maps.google.com/?q=%s,%s" target="_blank">%s</a>',
        $this->latitude, $this->longitude, e($this->address)
    );
})->asHtml(),

Testing around it

The widget is JS-only, so feature tests just see a text input. Assert the post-autocomplete state by posting the fields you expect (address, latitude, longitude) directly. For UI coverage use Nova Dusk and stub the network with a fixture rather than hitting Google in CI.

Gotchas and Tips

  • It's old and unmaintained. Last release was October 2019. Nova's asset pipeline and Vue version have moved several majors since. Expect to pin a compatible Nova version or, more realistically, budget time to fork the field and upgrade its compiled JS. Check package.json/nova.mix.js compatibility before committing to it.
  • API key exposure is the classic footgun. The key is injected into the browser. Restrict it in Google Cloud Console to your domain(s) and to the Places API + Maps JavaScript API only. Never ship a server key. Rotate it if this repo's history ever contained a real one.
  • Google's billing and SKU model changed after this package was written. Places Autocomplete sessions now need a session token to be billed correctly; older implementations fire per-keystroke requests and can get expensive. Wrap the widget in AutocompleteSessionToken if the package doesn't, or at minimum set a quota alert.
  • Config caching silently ignores .env. If the dropdown works locally but not on staging, it's almost always php artisan config:cache having frozen the old key. Re-cache on deploy.
  • CSP and script loading. If you have a Content-Security-Policy, maps.googleapis.com must be allow-listed for script-src and connect-src, and maps.gstatic.com/fonts.gstatic.com for styles. Symptom: silent failure, one console CSP violation.
  • Value overwritten on edit. Some versions hydrate the input from the model and then re-attach autocomplete, which can clear a saved address if the user touches the field without picking a suggestion. Verify on the edit form; if broken, guard the place_changed handler to only write when a real place is returned.
  • Modals and repeat renders. Inside Nova modals or repeatable fields, the Places widget can bind to the wrong input or leak listeners. Debounce initialisations and call the autocomplete teardown on component destroy; otherwise you get duplicate dropdowns.
  • Free-text vs. structured mismatch. Users can type anything and ignore the dropdown. If downstream logic depends on latitude/longitude, make the verified-place flag required in validation rather than assuming the widget guarantees it.
  • Debugging order: (1) is the script tag present in the DOM? (2) does the key appear in the field's meta? (3) any CSP or RefererNotAllowedMapError in the console? (4) does the input have the autocomplete class the component expects (renamed attributes break this)? That sequence resolves ~90% of reports.
  • Extension points worth knowing: subclass the field and override component() to point at your own compiled Vue file; use withMeta() to pass extra options (map defaults, component restrictions) into the JS; and override the config's script URL if you need loading=async or a region parameter (&region=US) for better suggestion quality.
  • Have an exit plan. With 17 stars and zero dependents, treat this as vendored code, not a dependency. Fork it into app/Nova/Fields (or a private package) so a future Nova upgrade doesn't leave you blocked on an abandoned repo — and so a Google API change is a one-file fix you control.
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.
jinas/dhivatar
store-doc/laravel-storage-driver
plus-it/laravel-translation-manager
diephp/perhaps
diephp/telegraph
osmaviation/spreadsheet
microsoft/fluentui-system-icons
mmccook/php-json-canonicalization-scheme
middlewares/firewall
qubiqx/montapacking
nyoncode/laravel-package-console
pnscripts/filament-broadquill-lite
pnscripts/laravel-sentfold-lite
vleap/mx-sdk-laravel-internal
twidpay/utils
htetmyathlaing/scout-elasticsearch-driver
artisan-toolbox/front-awesome
ls/client-assistant
users-au/laravel-client
cgv/localized-resource