Fields

Point Field

The point field captures geographic coordinates and stores them as an object with lat and lng number properties. By default, point fields are automatically synced to a spatial index powered by @convex-dev/geospatial, enabling efficient bounding-box and nearest-neighbor queries.

The underlying Convex validator is v.object({ lat: v.number(), lng: v.number() }), wrapped in v.optional() unless marked as required.

Setup

Point fields with spatial indexing (the default) require the @convex-dev/geospatial package:

1. Install the package

npm install @convex-dev/geospatial

2. Register in your Convex config

// convex/convex.config.ts
import { defineApp } from "convex/server";
import geospatial from "@convex-dev/geospatial/convex.config";
import vextro from "vextro/convex.config";

const app = defineApp();
app.use(geospatial);
app.use(vextro);

export default app;

3. Pass the component to your admin module

// convex/admin.ts
import { createVextroAdminModule } from "vextro/convex/admin";
import { components } from "./_generated/api";

const admin = createVextroAdminModule({
  query,
  mutation,
  components,
  geospatialComponent: components.geospatial,
  collectionDefinitions: vextro.collectionDefinitions,
});

If you use point fields with spatialIndex: false on all of them, the geospatial package is not required.

Config options

OptionTypeRequiredDefaultDescription
requiredbooleanfalseMakes the field required in the schema and admin UI
readOnlybooleanfalseRenders the field as non-editable
labelstring | functionField nameCustom label for the admin UI
descriptionstringHelp text displayed below the field label
showMapbooleantrueShow an interactive map picker
defaultZoomnumber10Default zoom level for the map
defaultCenter{ lat: number; lng: number }Default map center when no value is set
spatialIndexbooleantrueSync to @convex-dev/geospatial spatial index on create/update/delete
filterKeysRecord<string, string>{}Map of metadata key to top-level document field name for spatial query filtering
sortKeystringTop-level document field name to use as the spatial index sort key
conditionFieldConditionCondition for showing or hiding this field
sidebarbooleanfalsePlace this field in the document sidebar
listColumnbooleanfalseShow as a default column in list views

Example usage

import { f, defineVextroCollection } from "vextro";

export const stores = defineVextroCollection({
  slug: "stores",
  label: "Stores",
  collectionType: "content",
  tableName: "stores",
  fields: {
    name: f.text({ required: true }),
    category: f.select({
      required: true,
      options: ["restaurant", "retail", "service"],
    }),
    rating: f.number({ min: 0, max: 5 }),
    location: f.point({
      required: true,
      label: "Store Location",
      showMap: true,
      defaultZoom: 14,
      defaultCenter: { lat: 39.8283, lng: -98.5795 },
      description: "Click the map or enter coordinates manually",
      // Spatial index with filterable metadata
      filterKeys: { category: "category" },
      sortKey: "rating",
    }),
    // Display-only coordinate — no spatial indexing
    headquartersPin: f.point({
      spatialIndex: false,
      label: "HQ Coordinates",
      description: "For display on the about page only",
    }),
  },
});

Stored data format

The point field stores coordinates as a plain object:

{
  "lat": 27.9506,
  "lng": -82.4572
}

Spatial queries

For bounding-box queries, nearest-neighbor search, and other spatial operations using point field data, see the Geospatial Queries guide.

Admin options

When showMap is true (the default), the field renders an interactive map where editors can click to place a marker. The coordinate values update automatically. Manual lat/lng inputs are always available alongside the map for precise entry.

When showMap is false, only the manual latitude and longitude number inputs are shown. This is useful when map display is unnecessary or when embedding a map would add unwanted weight to the admin page.

The defaultCenter option sets the initial map viewport when the field has no value. Set this to the geographic center of your service area for a better editing experience.

Conditional display

deliveryPoint: f.point({
  label: "Delivery Coordinates",
  condition: { field: "fulfillmentType", equals: "delivery" },
  showMap: true,
  defaultZoom: 15,
})
Previous
Color