Library / SDK
airbnb/DeepLinkDispatch avatar
airbnb/DeepLinkDispatch

DeepLinkDispatch: annotation-based deep link routing for Android

A simple, annotation-based library for making deep link handling better on Android

4,416 stars414 forksKotlinLicense varies

At a glance

What is it?
DeepLinkDispatch maps URI templates to Activities or handler objects with an annotation processor, and can generate AndroidManifest.xml intent filters. It suits teams with many deep links; it is a poor fit for one-off links or projects that cannot adopt KSP.
Who is it for?
Adopt DeepLinkDispatch if you have many deep links and want the URI templates and their parameters checked at compile time; the handler path in particular turns silent string parsing into a typed function call. Do not adopt it for a single link, or if you cannot run KSP, since manifest generation requires KSP and will not work in an application module.
Can I use it commercially?
Not without permission. GitHub finds no licence file in the repository, and without a licence all rights are reserved by default: you may read the code but not reuse it. Check the README, or ask the authors, before using it.
Is it still maintained?
Yes. The repository last received commits 7 days ago.
What is it written in?
Mainly Kotlin, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 1, 2026, and from our analysis. They are not legal advice.

Editorial analysis

The problem DeepLinkDispatch solves on Android

On Android, a deep link is normally declared in AndroidManifest.xml as an intent-filter with a scheme, host and path, and the receiving Activity reads the incoming Intent and parses the URI by hand. That works until the number of links grows. Then the manifest entries drift out of sync with the code, path segments are extracted with string splitting, and a typo in a host name only shows up at runtime.

DeepLinkDispatch replaces the manual declaration with an annotation. According to the README, you register an Activity by annotating it with @DeepLink and a URI, and the library parses the URI and dispatches to the appropriate Activity along with any parameters specified in the URI. The audience is Android teams that already have a deep link surface large enough to be a maintenance problem, and who want the mapping between URL shape and destination to live in one place. It is not aimed at an app with a single link, where a manifest entry is less work than a build plugin.

How URI templates become dispatched intents

The core mechanism is a placeholder template. In the README example, the annotation value "example://example.com/deepLink/{id}" tells the processor that the path segment after /deepLink/ is a parameter named id. At runtime the incoming URI is matched against the registered templates, and the extracted values are placed into the Intent extras. The receiving Activity checks intent.getBooleanExtra(DeepLink.IS_DEEP_LINK, false) before reading the Bundle, so the same Activity can distinguish a deep link launch from a normal one.

One matching rule is easy to get wrong. The README states that only the scheme, host and path elements of the URI are used for matching; query parameters are not part of the match. They are still carried, and the README points to method callbacks and handler annotations as the places where they are useful. If you expect two URLs that differ only in a query string to route to different destinations, this design will not do that.

A second path skips Intents entirely. You annotate a Kotlin object extending com.airbnb.deeplinkdispatch.handler.DeepLinkHandler, and DeepLinkDispatch calls its handleDeepLink function with an argument class whose fields are annotated with @DeeplinkParam and typed as Path or Query. The README says the processor fails if the placeholders in the template and the fields in the argument class do not line up in either direction, which is the main reason to choose this path over the Activity one.

Installing DeepLinkDispatch and dispatching a first link

The README does not include a dependency coordinate for the library itself, so the exact artifact and version are not something this article can state; check the releases page and the sample modules in the repository for the current coordinates before adding anything. What the README does show is the shape of a first link, which is a class annotated with @DeepLink and an Activity that reads the extras.

java
@DeepLink("example://example.com/deepLink/{id}")
public class SampleActivity extends Activity {
  @Override protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    Intent intent = getIntent();
    if (intent.getBooleanExtra(DeepLink.IS_DEEP_LINK, false)) {
      Bundle parameters = intent.getExtras();
      String idString = parameters.getString("id");
    }
  }
}

After a build, the processor should have registered that template. Launching a URI matching example://example.com/deepLink/123 should land in SampleActivity with "123" available under the id key. If the Activity opens but the extras are empty, the usual cause is that the URI did not match the template at all, since only scheme, host and path participate in matching.

For a typed handler, the README gives this form:

kotlin
@DeepLink("foo://example.com/handlerDeepLink/{param1}?query1={queryParameter}")
object ProjectDeepLinkHandler : DeepLinkHandler<ProjectDeepLinkHandlerArgs>() {
    override fun handleDeepLink(parameters: ProjectDeepLinkHandlerArgs) {
    }
}

The argument class must declare @DeeplinkParam fields for every placeholder in the template, with DeepLinkParamType.Path or DeepLinkParamType.Query. The README notes that query values may be null because they are allowed to be absent from a matched URL, so nullable types are the safe choice for query parameters.

Manifest generation and its three preconditions

The feature that separates DeepLinkDispatch from a plain dispatcher is manifest generation. When deep links live in a library module, the build can emit the AndroidManifest.xml intent-filter entries automatically instead of you maintaining them by hand. The README lists three requirements, and they are restrictive. You must use KSP. The deep links must be in a module that does not apply the com.android.application Gradle plugin. And you must set activityClassFqn in the @DeepLink annotation, or in a @DeepLinkSpec when defining a custom deep link, so the processor knows which Activity the intent-filter belongs to.

Generation is wired in by a Gradle plugin, declared in the root buildscript and then applied per module:

groovy
apply plugin: 'com.airbnb.deeplinkdispatch.manifest-generation'

The README states the plugin must be applied after the Kotlin and Android plugins and cannot be applied to an Android application module. That last constraint shapes project structure: the annotated links have to sit in a library, which is a reasonable design for a large app but an awkward one for a small project that keeps everything in the app module. By default the generated filters use android.intent.action.VIEW with the DEFAULT and BROWSABLE categories, and the README says these can be overridden through the actions and categories attributes on @DeepLink or @DeepLinkSpec.

Where DeepLinkDispatch is the wrong tool

The matching rule is the sharpest limitation. Because query parameters do not participate in matching, a URL whose meaning lives in its query string cannot be routed by template alone. You have to route on the path and then branch inside the handler or Activity. For analytics-style links such as a product page with filters in the query, that pushes logic back into application code.

The second constraint is tooling. Manifest generation requires KSP and a non-application module, so a project still on KAPT, or one that wants its deep links declared in the app module, loses the generation feature. The repository does contain sample-kapt-library and sample-ksp-library, which suggests both processor paths exist, but the README attaches the generation requirements specifically to KSP.

The third is scale in the other direction. For an app with a handful of links, the annotation, the processor and the Gradle plugin are more moving parts than an intent-filter, and the build cost buys nothing. DeepLinkDispatch earns its place when link count makes manual manifest maintenance error-prone.

How it differs from declaring intent filters by hand

The obvious alternative is no library at all: write the intent-filter in AndroidManifest.xml and parse the URI in the Activity. The difference is where correctness lives. With hand-written filters, the URL pattern exists in XML and the parsing logic exists in Kotlin or Java, and nothing checks that the two agree. With DeepLinkDispatch, the template is the single source, and the handler path goes further by making the processor reject a mismatch between the template and the argument class. That check is the real feature; the annotation is just the syntax.

Hand-written filters do have advantages the library does not remove. There is no annotation processor in the build, no Gradle plugin applied after two other plugins, and no requirement to place links in a library module. For a team that ships infrequently or has a small link surface, the XML approach has fewer failure points. The trade is that every new link is a manual edit in two places, and the compiler will not tell you when they diverge.

Version upgrades and licensing

The repository carries CHANGELOG.md, RELEASING.md and UPGRADING.md at the top level, which means upgrade instructions are maintained separately from the README. Recent releases are 7.2.2, 7.2.1 and 7.1.1, all from January 2026, and the last push to the repository was on 2026-09-22. Anyone moving between major versions should read UPGRADING.md rather than infer the steps from the README, which documents the current API only.

The licence is not stated in the repository metadata available for this review, so the terms under which you may use DeepLinkDispatch in a closed-source app cannot be confirmed here. Check the LICENSE file or the repository's licence metadata before depending on it. This is a factual gap, not a legal opinion, and it is worth resolving before the library reaches a release build.

Editorial conclusion

Adopt DeepLinkDispatch if you have many deep links and want the URI templates and their parameters checked at compile time; the handler path in particular turns silent string parsing into a typed function call. Do not adopt it for a single link, or if you cannot run KSP, since manifest generation requires KSP and will not work in an application module. Before committing, verify how your module structure fits the manifest generation requirements, and check UPGRADING.md for the migration steps between major versions.

Frequently asked questions

What is DeepLinkDispatch used for?

It registers Android deep links declaratively: you annotate an Activity or a DeepLinkHandler object with @DeepLink and a URI template, and the library parses the incoming URI and dispatches it with the parameters from the URL.

Does DeepLinkDispatch match query parameters in a deep link?

No. The README states that only the scheme, host and path elements of the URI are used for matching; query parameters are not used for matching, though they are handled and available in the deep link meta data.

What are the requirements for DeepLinkDispatch manifest generation?

The README lists three: you must use KSP, the deep links must be in a module that does not apply the com.android.application Gradle plugin, and you must specify activityClassFqn in the @DeepLink annotation or in a @DeepLinkSpec.

Which parameter types does DeepLinkDispatch convert automatically?

The README says query parameter conversion is supported for nullable and non-nullable versions of Boolean, Int, Long, Short, Byte, Double, Float and String, as well as the same types in Java, with other types handled through custom type conversion.

Official sources

  1. airbnb/DeepLinkDispatch on GitHub
  2. Issues
  3. Project website
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/airbnb-deeplinkdispatch.svg)](https://hysenlabs.com/projects/airbnb-deeplinkdispatch)