Skip to main content

Migration Guide: v0.13.x to v0.14.0

Non-native, compile-time only. This release removes the legacy BackgroundLocation default export. There are zero runtime or native behavior changes -- only the shape of your imports. Your app will fail to build until the migration below is applied, but nothing can break silently at runtime.

TL;DR

Replace the default import and dotted call sites with named imports. For most codebases, a single find-and-replace is enough:

# 1. Delete every default import of the library
# find: import BackgroundLocation from '@gabriel-sisjr/react-native-background-location';
# action: remove, then add a named import at the top of the file (see step 4 below).

# 2. Strip the object prefix from every call site
# find: BackgroundLocation\.(startTracking|stopTracking|isTracking|getLocations|clearTrip|updateNotification)
# replace: $1

One-line GNU sed codemod for the call sites (safe on macOS with gsed, or Linux):

gsed -i -E 's/BackgroundLocation\.(startTracking|stopTracking|isTracking|getLocations|clearTrip|updateNotification)/\1/g' $(git ls-files '*.ts' '*.tsx')

BSD sed equivalent (macOS default sed):

find . -type f \( -name '*.ts' -o -name '*.tsx' \) -not -path '*/node_modules/*' \
-exec sed -i '' -E 's/BackgroundLocation\.(startTracking|stopTracking|isTracking|getLocations|clearTrip|updateNotification)/\1/g' {} +

Portable Node.js one-liner (no GNU/BSD split, works everywhere node is installed):

node -e "
const fs=require('fs'),path=require('path');
const walk=d=>fs.readdirSync(d,{withFileTypes:true}).flatMap(e=>{
const p=path.join(d,e.name);
if(e.isDirectory())return e.name==='node_modules'||e.name==='.git'?[]:walk(p);
return /\.(ts|tsx)$/.test(e.name)?[p]:[];
});
const re=/BackgroundLocation\.(startTracking|stopTracking|isTracking|getLocations|clearTrip|updateNotification)/g;
for(const f of walk('.')){
const before=fs.readFileSync(f,'utf8');
const after=before.replace(re,'\$1');
if(before!==after){fs.writeFileSync(f,after);console.log('updated',f);}
}
"

After the codemod, manually update the default-import line in each touched file to a named import (see Step-by-Step Migration).


Quick Migration Checklist

  • Update @gabriel-sisjr/react-native-background-location to ^0.14.0
  • Run the sed / Node codemod above to strip BackgroundLocation. prefixes
  • Replace every import BackgroundLocation from ... with a named import list
  • Run yarn typecheck -- any missed symbol will surface as Cannot find name
  • Run the app on Android + iOS to confirm nothing changed at runtime (nothing should)
  • Clear IDE caches if auto-import still suggests the old default (see Common Pitfalls)

Why This Changed

  • Tree-shaking. The default export bundled all tracking methods into one object, forcing every consumer to pull the entire tracking API even if they only called isTracking(). Named exports let modern bundlers (Metro with RAM bundles, Hermes bytecode, webpack in test environments) drop unused methods. For a typical app that only uses 3-4 methods, this is a measurable bundle-size win.
  • Consistency. Geofencing methods (addGeofence, removeGeofence, etc.) shipped in v0.11.0 as named exports. Hooks (useLocationPermissions, useBackgroundLocation, etc.) have always been named exports. Mixing a default export with named exports forced consumers to remember two import styles and confused IDE auto-import.
  • Better IDE auto-import. VSCode and IntelliJ now auto-suggest startTracking from the top of the file, the same way they suggest useState or any other hook. No more BackgroundLocation. prefix lookups.
  • Cleaner public API surface. src/index.tsx is now a lean facade. All internal helpers (module probing, enum-to-string conversion, geofence validation, geofence serialization) moved to src/utils/. GeofenceError moved to src/errors/. None of that is user-facing, but it makes the library easier to reason about and audit.

No native (Android/iOS) code changed. Method signatures, return types, and runtime behavior are identical.


Before / After

Tracking Lifecycle

// Before (v0.13.x)
import BackgroundLocation from '@gabriel-sisjr/react-native-background-location';

const tripId = await BackgroundLocation.startTracking('delivery-42', {
accuracy: LocationAccuracy.HIGH_ACCURACY,
distanceFilter: 10,
});
const { active } = await BackgroundLocation.isTracking();
const points = await BackgroundLocation.getLocations(tripId);
await BackgroundLocation.stopTracking();
await BackgroundLocation.clearTrip(tripId);
// After (v0.14.0)
import {
startTracking,
stopTracking,
isTracking,
getLocations,
clearTrip,
LocationAccuracy,
} from '@gabriel-sisjr/react-native-background-location';

const tripId = await startTracking('delivery-42', {
accuracy: LocationAccuracy.HIGH_ACCURACY,
distanceFilter: 10,
});
const { active } = await isTracking();
const points = await getLocations(tripId);
await stopTracking();
await clearTrip(tripId);

Dynamic Notification Updates

// Before
import BackgroundLocation from '@gabriel-sisjr/react-native-background-location';
await BackgroundLocation.updateNotification(
'Delivery #42',
'Arriving in 5 min'
);
// After
import { updateNotification } from '@gabriel-sisjr/react-native-background-location';
await updateNotification('Delivery #42', 'Arriving in 5 min');

Geofencing

Geofencing methods were already named exports in v0.13.x. No change required. If your code already looks like this, it will keep working:

import {
addGeofence,
removeGeofence,
getActiveGeofences,
configureGeofenceNotifications,
GeofenceError,
GeofenceErrorCode,
} from '@gabriel-sisjr/react-native-background-location';

try {
await addGeofence({
identifier: 'warehouse-1',
latitude: -23.5505,
longitude: -46.6333,
radius: 150,
transitions: ['enter', 'exit'],
});
} catch (err) {
if (
err instanceof GeofenceError &&
err.code === GeofenceErrorCode.DUPLICATE_IDENTIFIER
) {
// handle duplicate
}
}

Hooks

Hooks were always named exports. No change required:

import {
useLocationPermissions,
useBackgroundLocation,
useLocationUpdates,
useLocationTracking,
useGeofencing,
useGeofenceEvents,
useGeofencePermissions,
} from '@gabriel-sisjr/react-native-background-location';

Error Handling

// Before
import BackgroundLocation from '@gabriel-sisjr/react-native-background-location';

try {
await BackgroundLocation.startTracking();
} catch (err) {
console.error(err);
}
// After
import { startTracking } from '@gabriel-sisjr/react-native-background-location';

try {
await startTracking();
} catch (err) {
console.error(err);
}

Step-by-Step Migration

A real upgrade from v0.13.x to v0.14.0 on a medium-sized app should take under 2 minutes. Here is the recipe.

  1. Bump the dependency.

    yarn add @gabriel-sisjr/react-native-background-location@^0.14.0
    # or
    npm install @gabriel-sisjr/react-native-background-location@^0.14.0
  2. Strip the BackgroundLocation. prefix from every call site. Use the codemod from the TL;DR section. One command, global, reversible via git.

  3. Replace every default import with a named import. The codemod leaves the import BackgroundLocation from ... lines intact on purpose -- you need to decide which symbols to import per file. For each file that used to do:

    import BackgroundLocation from '@gabriel-sisjr/react-native-background-location';

    Replace it with the set of functions that file actually uses, for example:

    import {
    startTracking,
    stopTracking,
    updateNotification,
    } from '@gabriel-sisjr/react-native-background-location';

    VSCode tip: delete the default-import line, then type a function name like startTracking -- the auto-import suggestion will add the named import for you.

  4. Run the TypeScript checker. Any missed symbol or typo will surface as Cannot find name 'BackgroundLocation' or Cannot find name 'startTracking':

    yarn typecheck
  5. Rebuild and smoke test. Because no native behavior changed, a simple cold-start of the app on Android + iOS is enough:

    yarn android # and/or yarn ios

    Start a trip, confirm points arrive, stop the trip. Done.


Common Pitfalls

A few gotchas that have been reported. If migration feels like it should be working but isn't, check here first.

  1. Mixed default + named import on the same line. Before: import BackgroundLocation, { useLocationPermissions } from '...'; The codemod only touches call sites, not imports, so this line survives the sed pass but will break the build. Remove the default identifier and merge everything into the named list:

    import {
    useLocationPermissions,
    startTracking,
    stopTracking,
    } from '@gabriel-sisjr/react-native-background-location';
  2. ESLint import/no-default-export + auto-fix keeps resurrecting the old import. If you had a custom ESLint rule or a shared config that auto-imports the default export on save, disable or remove it before running the codemod. Otherwise the save hook undoes your work.

  3. IDE auto-import still suggesting BackgroundLocation. VSCode / WebStorm cache resolved module exports. If auto-import suggestions look stale after the upgrade:

    • VSCode: Cmd+Shift+P -> TypeScript: Restart TS Server
    • WebStorm: File -> Invalidate Caches -> Invalidate and Restart
  4. Metro cache serving the old module shape. After the upgrade, if your app crashes with undefined is not an object (evaluating 'BackgroundLocation.startTracking') at runtime (i.e. your codemod missed a site), restart Metro with a clean cache:

    yarn start --reset-cache
  5. CI builds passing locally, failing on the runner. Your local node_modules got bumped but the runner's cache still has v0.13.x. Bust the CI cache key (e.g. rev the yarn.lock hash in your cache step) or clear the CI cache manually.


Affected Symbols

Every symbol that was previously accessed via the BackgroundLocation default export. All geofencing methods were already named exports in v0.13.x and are included here for completeness -- they need no code change in your app.

SymbolBefore (v0.13.x)After (v0.14.0)Status
startTrackingBackgroundLocation.startTracking(...)import { startTracking } then startTracking(...)Migrated
stopTrackingBackgroundLocation.stopTracking()import { stopTracking } then stopTracking()Migrated
isTrackingBackgroundLocation.isTracking()import { isTracking } then isTracking()Migrated
getLocationsBackgroundLocation.getLocations(id)import { getLocations } then getLocations(id)Migrated
clearTripBackgroundLocation.clearTrip(id)import { clearTrip } then clearTrip(id)Migrated
updateNotificationBackgroundLocation.updateNotification(t, x)import { updateNotification } then updateNotification(t, x)Migrated
addGeofenceimport { addGeofence } (already named)import { addGeofence } (unchanged)No change
addGeofencesimport { addGeofences } (already named)import { addGeofences } (unchanged)No change
removeGeofenceimport { removeGeofence } (already named)import { removeGeofence } (unchanged)No change
removeGeofencesimport { removeGeofences } (already named)import { removeGeofences } (unchanged)No change
removeAllGeofencesimport { removeAllGeofences } (already named)import { removeAllGeofences } (unchanged)No change
getActiveGeofencesimport { getActiveGeofences } (already named)import { getActiveGeofences } (unchanged)No change
getMaxGeofencesimport { getMaxGeofences } (already named)import { getMaxGeofences } (unchanged)No change
getGeofenceTransitionsimport { getGeofenceTransitions } (already named)import { getGeofenceTransitions } (unchanged)No change
clearGeofenceTransitionsimport { clearGeofenceTransitions } (already named)import { clearGeofenceTransitions } (unchanged)No change
configureGeofenceNotificationsimport { configureGeofenceNotifications } (already named)import { configureGeofenceNotifications } (unchanged)No change
getGeofenceNotificationConfigimport { getGeofenceNotificationConfig } (already named)import { getGeofenceNotificationConfig } (unchanged)No change
GeofenceErrorimport { GeofenceError } (already named)import { GeofenceError } (unchanged, now backed by src/errors/)No change
All hooksimport { useBackgroundLocation } (always named)UnchangedNo change
All enumsimport { LocationAccuracy } (always named)UnchangedNo change
All typesimport type { TrackingOptions } (always named)UnchangedNo change

Internal Refactor (For the Curious)

src/index.tsx is no longer a catch-all. Internal helpers moved out:

  • src/utils/isNativeModuleAvailable.ts -- non-throwing TurboModule probe (simulator-safe)
  • src/utils/moduleCheck.ts -- assertNativeModuleAvailable() (throwing variant used by geofence methods)
  • src/utils/trackingOptionsMapper.ts -- toTrackingOptionsSpec() enum-to-string conversion for the Codegen bridge
  • src/utils/geofenceValidation.ts -- validateGeofenceRegion() runtime validation
  • src/utils/geofenceSerialization.ts -- prepareGeofenceRegion() / serializeGeofenceRegion() JSON helpers
  • src/utils/objectUtils.ts -- extractDefinedProperties() generic utility consumed by hooks
  • src/errors/GeofenceError.ts -- GeofenceError class with GeofenceErrorCode discriminator

All of these are internal and not part of the public API. Do not import from src/utils/* or src/errors/* directly -- the only stable surface is the package root, @gabriel-sisjr/react-native-background-location.


Need Help?

If the codemod misses a case or you hit a migration scenario not covered here:

The migration is intentionally small in scope. If you are upgrading from an older version, go through v0.12.0 migration first, then apply this guide.