When using the Kotlin DSL to construct your graph, keeping destinations and navigation events in a single file can be difficult to maintain. This is especially true if you have multiple independent features.

## Extract destinations

You should move your destinations into[`NavGraphBuilder`](https://developer.android.com/reference/kotlin/androidx/navigation/NavGraphBuilder)extension functions. They should live close to the routes which define them, and the screens that they display. For example, consider the following app-level code that creates a destination which shows a list of contacts:  

    // MyApp.kt

    @Serializable
    object Contacts

    @Composable
    fun MyApp() {
      ...
      NavHost(navController, startDestination = Contacts) {
        composable<Contacts> { ContactsScreen( /* ... */ ) }
      }
    }

You should move the navigation-specific code into a separate file:  

    // ContactsNavigation.kt

    @Serializable
    object Contacts

    fun NavGraphBuilder.contactsDestination() {
        composable<Contacts> { ContactsScreen( /* ... */ ) }
    }

    // MyApp.kt

    @Composable
    fun MyApp() {
      ...
      NavHost(navController, startDestination = Contacts) {
         contactsDestination()
      }
    }

The routes and destination definitions are now separate from the main app and you can update them independently. The main app is only dependent on a single extension function. In this case, that is`NavGraphBuilder.contactsDestination()`.

The`NavGraphBuilder`extension function forms the bridge between a stateless screen-level composable function and Navigation-specific logic. This layer can also define where the state comes from and how you handle events.
| **Note:** This is essentially the same approach as when you move screens to different composable functions instead of defining them in one monolithic composable function.

## Example

The following snippet introduces a new destination to display a contact's details, and updates the existing contact list destination to[expose a navigation event](https://developer.android.com/guide/navigation/use-graph/navigate#nav-from-composable)to display the contact's details.

Here's a typical set of screens that can be`internal`to their own module, so that other modules cannot access them:  

    // ContactScreens.kt

    // Displays a list of contacts
    @Composable
    internal fun ContactsScreen(
      uiState: ContactsUiState,
      onNavigateToContactDetails: (contactId: String) -> Unit
    ) { ... }

    // Displays the details for an individual contact
    @Composable
    internal fun ContactDetailsScreen(contact: ContactDetails) { ... }

### Create destinations

The following[`NavGraphBuilder`](https://developer.android.com/reference/kotlin/androidx/navigation/NavGraphBuilder)extension function creates a destination which shows the`ContactsScreen`composable. In addition, it now connects the screen with a`ViewModel`that provides the screen UI state and handles the screen-related business logic.

Navigation events, such as navigating to the contact details destination, are exposed to the caller rather than being handled by the`ViewModel`.  

    // ContactsNavigation.kt

    @Serializable
    object Contacts

    // Adds contacts destination to `this` NavGraphBuilder
    fun NavGraphBuilder.contactsDestination(
      // Navigation events are exposed to the caller to be handled at a higher level
      onNavigateToContactDetails: (contactId: String) -> Unit
    ) {
      composable<Contacts> {
        // The ViewModel as a screen level state holder produces the screen
        // UI state and handles business logic for the ConversationScreen
        val viewModel: ContactsViewModel = hiltViewModel()
        val uiState = viewModel.uiState.collectAsStateWithLifecycle()
        ContactsScreen(
          uiState,
          onNavigateToContactDetails
        )
      }
    }

You can use the same approach to create a destination which displays the`ContactDetailsScreen`. In this case, instead of obtaining the UI state from a view model, you can obtain it directly from the`NavBackStackEntry`.  

    // ContactsNavigation.kt

    @Serializable
    internal data class ContactDetails(val id: String)

    fun NavGraphBuilder.contactDetailsScreen() {
      composable<ContactDetails> { navBackStackEntry ->
        ContactDetailsScreen(contact = navBackStackEntry.toRoute())
      }
    }

### Encapsulate navigation events

In the same way that you encapsulate destinations, you can encapsulate navigation events to avoid exposing route types unnecessarily. Do this by creating extension functions on`NavController`.  

    // ContactsNavigation.kt

    fun NavController.navigateToContactDetails(id: String) {
      navigate(route = ContactDetails(id = id))
    }

### Bring it together

The navigation code for displaying contacts is now cleanly separated from the app's navigation graph. The app needs to:

- Call`NavGraphBuilder`extension functions to create destinations
- Connect those destinations by calling`NavController`extension functions for navigation events

    // MyApp.kt

    @Composable
    fun MyApp() {
      ...
      NavHost(navController, startDestination = Contacts) {
         contactsDestination(onNavigateToContactDetails = { contactId ->
            navController.navigateToContactDetails(id = contactId)
         })
         contactDetailsDestination()
      }
    }

## In summary

- Encapsulate your navigation code for a related set of screens by placing it in a separate file
- Expose destinations by creating extension functions on`NavGraphBuilder`
- Expose navigation events by creating extension functions on`NavController`
- Use`internal`to keep screens and route types private