> ## Documentation Index
> Fetch the complete documentation index at: https://mahmoud-b28887f9.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Display a PDF in a Compose Multiplatform app screen

> Step-by-step guide to picking a PDF source, configuring the viewer, creating viewer state with rememberPdfViewerState, and rendering KPdfViewer.

KPDF renders PDF documents through a small set of composables that share a single state holder. To display a PDF you choose a source, build a viewer configuration, create the state with `rememberPdfViewerState`, and drop `KPdfViewer` into your layout. The steps below walk through each piece in order.

<Steps>
  <Step title="Choose a KPdfSource">
    `KPdfSource` describes where the PDF comes from. Pick the variant that matches your data.

    ```kotlin theme={null}
    // Load from a remote URL (optional auth headers supported)
    val source = KPdfSource.Url(
        url = "https://example.com/document.pdf",
        headers = mapOf("Authorization" to "Bearer token"),
    )

    // Load from raw bytes already in memory
    val source = KPdfSource.Bytes(pdfBytes)

    // Load from a Base64-encoded string
    val source = KPdfSource.Base64(base64String)
    ```

    Pass this value down to your composable as a stable parameter so the SDK does not reload the document on every recomposition.
  </Step>

  <Step title="Create a KPdfViewerConfig with the builder">
    `KPdfViewerConfig` controls runtime behavior — zoom limits, gesture toggles, and cache sizes. Build it once inside a `remember` block so the reference stays stable.

    ```kotlin theme={null}
    val viewerConfig = remember {
        KPdfViewerConfig.builder()
            .enableZoom(true)
            .enableSwipe(true)
            .zoomRange(minZoom = 1f, maxZoom = 5f)
            .doubleTapZoom(2f)
            .ramCacheSize(6)
            .diskCacheSize(24)
            .preloadPageCount(2)
            .build()
    }
    ```

    | Builder option     | Description                               |
    | ------------------ | ----------------------------------------- |
    | `enableZoom`       | Allow pinch-to-zoom gestures              |
    | `enableSwipe`      | Allow swipe-to-navigate gestures          |
    | `zoomRange`        | Minimum and maximum zoom factors          |
    | `doubleTapZoom`    | Zoom factor applied on a double tap       |
    | `ramCacheSize`     | Number of pages to keep in RAM            |
    | `diskCacheSize`    | Number of pages to keep on disk           |
    | `preloadPageCount` | Pages to render ahead of the current page |
  </Step>

  <Step title="Call rememberPdfViewerState">
    `rememberPdfViewerState` creates a `KPdfViewerState`, opens the document, and binds platform save/open effects for you. Call it at the top of your composable.

    ```kotlin theme={null}
    val viewerState = rememberPdfViewerState(
        source = stableSource,
        config = viewerConfig,
    )
    ```

    The state is keyed on `source` and `config`. Passing a new config object on every recomposition recreates the state and resets all transient flows, including `openDocumentState` and `saveState`.
  </Step>

  <Step title="Add KPdfViewer to your layout">
    Place `KPdfViewer` anywhere in your composable tree. It reads the active page and zoom level directly from `KPdfViewerState`.

    ```kotlin theme={null}
    KPdfViewer(
        state = viewerState,
        modifier = Modifier.fillMaxSize(),
    )
    ```

    For a production screen that also includes a toolbar and thumbnail strip, use the complete layout below.

    ```kotlin theme={null}
    @Composable
    fun FullPdfScreen(source: KPdfSource) {
        val viewerState = rememberPdfViewerState(
            source = source,
            config = KPdfViewerConfig.builder()
                .preloadPageCount(1)
                .diskCacheSize(50)
                .build(),
        )
        var thumbnailsVisible by remember { mutableStateOf(true) }
        val scope = rememberCoroutineScope()

        Column(modifier = Modifier.fillMaxSize()) {
            KPdfViewerToolbar(
                state = viewerState,
                isThumbnailStripVisible = thumbnailsVisible,
                onThumbnailToggle = { thumbnailsVisible = it },
                onShareClick = {
                    scope.launch {
                        viewerState.exportPdf().onSuccess { bytes ->
                            sharePdfBytes(bytes)
                        }
                    }
                },
                modifier = Modifier
                    .fillMaxWidth()
                    .padding(12.dp),
            )

            KPdfViewer(
                state = viewerState,
                modifier = Modifier
                    .fillMaxWidth()
                    .weight(1f),
            )

            if (thumbnailsVisible) {
                KPdfThumbnailStrip(
                    state = viewerState,
                    onPageClick = { pageIndex ->
                        viewerState.goToPage(pageIndex)
                    },
                    modifier = Modifier
                        .fillMaxWidth()
                        .height(172.dp)
                        .padding(horizontal = 12.dp),
                )
            }
        }
    }
    ```
  </Step>
</Steps>

<Note>
  Keep `source` and `config` stable. If you construct `KPdfViewerConfig` inline — outside a `remember` block — Compose recreates it on every recomposition. This causes `rememberPdfViewerState` to produce a new state instance, which resets `openDocumentState`, `saveState`, and other transient flows back to their initial values.
</Note>
