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 Address Field Laravel Package

digitalcloud/nova-address-field

Dedicated address field for Laravel Nova forms with autocomplete and geocoding support, so you can capture, store, and display structured addresses without custom components. A simple drop-in field that keeps admin forms tidy and saves development time.

View on GitHub
Deep Wiki
Context7

Getting Started

Reality check first. Last release April 2019, ~10 stars. That places it firmly in the Nova 1/2 era (Vue 2, Nova::script() asset registration). It will install fine, but treat it as a starting point to vendor/fork rather than a dependency you pin and forget. Budget an hour to read vendor/digitalcloud/nova-address-field/src before wiring anything into a real form — the whole package is one field class plus one Vue component.

1. Install

composer require digitalcloud/nova-address-field

2. Configure the Google key. You need a key with both Maps JavaScript API and Places API enabled (enabling only one is the #1 cause of "field renders, no suggestions"). Publish whatever config the package ships:

php artisan vendor:publish --provider="DigitalCloud\NovaAddressField\NovaAddressFieldServiceProvider"

Then set the key in the published config (or config/services.php if the package reads from there — check src/ for config('...') calls). In dev, restrict the key by HTTP referrer (http://localhost:*, your staging domain) so a leaked key can't be used elsewhere.

3. Register the field

use DigitalCloud\NovaAddressField\Address; // verify exact FQCN with `ls src/`

public function fields(Request $request)
{
    return [
        ID::make()->sortable(),

        Address::make('address_line_1')
            ->setFields([
                'line1'       => 'address_line_1',
                'city'        => 'city',
                'postal_code' => 'postal_code',
                'country'     => 'country',
            ])
            ->rules('required'),
    ];
}

Where to look first. The field class (src/*.php) tells you the attribute name, the setFields mapping shape, and which meta (API key, options) is passed to the front end. The Vue component (resources/js/components/*.vue) tells you what actually gets submitted — that is the single most important thing to confirm before you design your schema.

First use case. One addresses table with address_line_1, city, postcode, country. Add the field to a resource, create a record through the Nova UI with a real key, then dd() the request payload. If the sibling columns are empty, jump straight to the first Gotcha below.

Implementation Patterns

Store a formatted string and the components. A formatted_address column is what you'll actually render in tables and PDFs; the parsed components are what you'll query. Map the primary attribute to the formatted string and let the others fill in:

Address::make('formatted_address')
    ->setFields([
        'line1'    => 'address_line_1',
        'line2'    => 'address_line_2',
        'city'     => 'city',
        'postcode' => 'postcode',
        'country'  => 'country',
    ])
    ->hideFromIndex()
    ->rules('required', 'max:255'),

Normalize the mapping in one place. Don't copy the setFields array into five resources. Either a small trait/helper:

trait HasAddressField
{
    protected function addressField(string $attribute = 'formatted_address'): Address
    {
        return Address::make($attribute)->setFields(config('app.address_map'));
    }
}

…or extend the field and hard-code your convention:

class CompanyAddress extends Address
{
    public function __construct($name, $attribute = null, $resolveCallback = null)
    {
        parent::__construct($name ?: 'formatted_address', $attribute, $resolveCallback);

        $this->setFields([
            'line1' => 'street', 'city' => 'city',
            'postcode' => 'zip', 'country' => 'country_code',
        ]);
    }
}

That second option is the cleanest extension point this package has — you keep your schema convention out of every resource.

Don't trust the client for geodata. If you need lat/lng, get it server-side in your model's saving/saved observer via the Geocoding API and cache the result. Two reasons: the package's client-side geometry may not be exposed to you at all, and a client-submitted coordinate is trivially forged.

Validate the columns you actually persist. ->rules('required') on the field validates the field's own attribute. Add Laravel-level rules on the model or a FormRequest for postcode, country_code, etc. — Nova won't do it for you.

Detail views and indexes. This field is form-oriented. On index, show a plain Text::make('formatted_address'); on detail, same. Keep the address field ->onlyOnForms() so you don't ship the Google JS to list screens.

Testing. Never let CI hit Google. Feature-test the form submission with a fake payload; if you need browser coverage, run Dusk against staging with a restricted key. The autocomplete interaction is brittle to automate — assert on persistence, not on the dropdown.

Gotchas and Tips

Persisting sibling columns is the big one. The extra inputs rendered by the Vue component live inside that field's value, not as independent Nova fields. Depending on how the component is written, only the primary attribute is submitted, and city/postcode/country silently stay null. Test this on day one. If they don't persist, fix it by overriding fill()/resolveUsing() on the field, or by expanding the payload in the Vue component's filled/handleChange. Do not discover this after migrating production data.

Billing will surprise you. A 2019 field almost certainly uses the legacy Places Autocomplete endpoint without session tokens, which means you're billed per keystroke rather than per session. Google has since moved to Places API (New) with different endpoints and SKUs. For a low-traffic admin panel this is cents; for a busy one, or if the field is ever exposed outside Nova, it isn't. Set a budget alert on the key before you go live.

The key is in the browser. That's inherent to Places JS. Restrict it by HTTP referrer and by API. Never reuse a server-side key here — if a server key leaks into a Nova field's meta, it's in the page source for every logged-in admin.

Nova 4/5 compatibility. Nova's front-end stack and asset pipeline changed substantially after 2019. Expect the compiled JS to misbehave or fail to load. The pragmatic path: composer require from your own fork via a repositories VCS entry, or copy the package into nova-components/ as a custom field and rebuild the Vue component against your Nova version. Copying is usually faster than fighting the build.

Debugging a field that renders but never suggests anything. In order: (1) browser console for JS errors from the old bundle; (2) Network tab — is the Maps JS actually loading, or is it a RefererNotAllowedMapError; (3) the key's enabled APIs; (4) whether the config value is reaching the component at all (->meta() in the field class).

Stale components after manual edits. The autocomplete overwrites all siblings when a suggestion is picked, but users will type over city afterwards. Re-geocode/normalize on save rather than trusting the last-known autocomplete result.

Sortable/searchable won't work. It's a composite, JS-driven field. Anything that needs sorting or searching should point at a real column and a plain Text field.

Schema choice matters. If the component submits a JSON blob, your column must be json, not string. Getting this wrong gives you a silent truncation or a 500 on save that's annoying to trace. Check the payload shape first, then write the migration.

Fork it deliberately. Given the release age and star count, assume no upstream fixes. Pin to your fork's commit SHA, keep a short README note of what you changed, and put the API key in config/ (env-backed) — never in the published config file committed to the repo.

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.
pharaonic/laravel-notification-dependencies
beautinow/translug
dev-brainiac/bot-cloaked
octoberrain/html
octoberrain/extension
octoberrain/exception
octoberrain/contracts
frankyso/ipaymu
3mdikram/worldmoneywords
snowsoft/config
itmosoft/laravel-subscriptions
larva/laravel-ai-qwen
adachsoft/api-version-bundle
cgv/localized-resource
noith/settings
kda/laravel-package
kda/filament-dynamic-navigation
bypersoft/signbox
pairus/product-data
fayne/laravel-filament-cms-core