> ## Documentation Index
> Fetch the complete documentation index at: https://developer.suki.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Build Dictation into a Chart Field

> Use-case tutorial: Mount Dictation SDK in-field mode on a chart textarea in React with one shared DictationClient

<Callout icon="info-circle" color="#FFE148">
  **What you will build**

  <p className="doc-tutorial-meta" aria-label="15 min, Intermediate">
    <Badge size="sm" color="gray" icon="clock">15 min</Badge> |
    <Badge size="sm" color="gray" icon="user-graduate">Intermediate</Badge>
  </p>

  * One shared Dictation client for the chart screen
  * A clinical notes field with a stable field id
  * In-field Dictation that writes committed text back into that field
</Callout>

<Tip>
  **Using an AI coding tool?**

  Copy the following prompt to add the Suki developer documentation as a skill and [MCP server](/documentation/references/mcp) to your tool for better AI-assisted coding during the integration process. For all AI options (contextual menu, `llms.txt`, and `skill.md`), refer to [AI-optimized documentation](/documentation/references/ai-optimized-documentation).
</Tip>

<Prompt description="Install the Suki developer docs as a skill to get context on Suki's developer tools, APIs, and SDKs. Add the [MCP server](/documentation/references/mcp) for documentation search." icon="gear" iconType="regular" actions={["copy", "cursor"]}>
  Install the Suki developer docs as skill to get context on Suki's developer tools, APIs, and SDKs.

  npx skills add [https://developer.suki.ai](https://developer.suki.ai)

  Then add the Suki developer docs MCP server for access to documentation search. Follow the MCP instructions at [https://developer.suki.ai/documentation/references/mcp](https://developer.suki.ai/documentation/references/mcp).
</Prompt>

In this tutorial, you add Dictation SDK <strong>in-field</strong> mode to a chart note textarea in React. You create one <strong><code>DictationClient</code></strong>, wrap the screen with <strong><code>DictationProvider</code></strong>, and mount <strong><code>\<Dictation></code></strong> only while that field is active.

By the end of this tutorial, starting Dictation inserts text into your chart field through <strong><code>onSubmit</code></strong>.

<Callout title="Beta" color="orange" icon="flask">
  The Dictation SDK is in beta. Confirm package versions and iframe requirements with your Suki contact before production use.
</Callout>

<span id="prerequisites" />

## Prerequisites

Before you begin, make sure you have:

* Completed [Partner onboarding](/documentation/get-started/partner-onboarding).
* A React 18+ browser app that meets [Dictation SDK prerequisites](/dictation-sdk/prerequisites) (CSP and iframe hosts).
* Staging <code>partnerId</code> and <code>partnerToken</code> values.

<Tip>
  Reuse one client for the page. Creating a new <code>DictationClient</code> on every render tears the iframe down and makes the field feel unstable.
</Tip>

<span id="architecture" />

## Architecture

```mermaid actions={false} theme={"theme":{"light":"github-dark","dark":"material-theme-darker"}}
%%{init: {'theme':'base', 'themeVariables': { 'primaryColor':'#FFE148','primaryTextColor':'#111827','primaryBorderColor':'#D4A017','lineColor':'#6F5410','secondaryColor':'#FFF394','tertiaryColor':'#FFFADE','tertiaryTextColor':'#111827','tertiaryBorderColor':'#D4A017','arrowheadColor':'#6F5410','fontSize':'14px','edgeLabelBackground':'#FFFADE'}}}%%
flowchart LR
  A[SukiAuthManager] --> B[DictationClient]
  B --> C[DictationProvider]
  C --> D[In-field Dictation]
  D --> E[Chart textarea]
```

<span id="project-setup" />

## Project setup

1. Use a React 18+ browser app that meets [Dictation SDK prerequisites](/dictation-sdk/prerequisites) (CSP and iframe hosts).
2. Install the required dependencies:

```shell theme={"theme":{"light":"github-dark","dark":"material-theme-darker"}}
# pnpm
pnpm add @suki-sdk/core @suki-sdk/dictation @suki-sdk/dictation-react

# npm
npm install @suki-sdk/core @suki-sdk/dictation @suki-sdk/dictation-react
```

3. Configure your credentials:

   <Callout icon="gear" color="#929292">
     * Keep staging `partnerId` and `partnerToken` ready.
     * Reuse one `DictationClient` for the page. Creating a new client on every render tears the iframe down.
   </Callout>

<span id="tutorial-steps" />

## Tutorial steps

Work through each step in order. Read the explanation, review the code example, then continue to the next step. The full code example is available in the accordion below.

<Steps>
  <Step title="Create One Auth Manager and DictationClient">
    Build `SukiAuthManager` and `DictationClient` once inside `useMemo`. Do not create a new client per render or per field.

    ```tsx theme={"theme":{"light":"github-dark","dark":"material-theme-darker"}}
    import { useMemo } from "react";
    import { SukiAuthManager } from "@suki-sdk/core";
    import { DictationClient } from "@suki-sdk/dictation";

    function useChartDictationClient() {
    return useMemo(() => {
    const authManager = new SukiAuthManager({
      partnerId: "YOUR_PARTNER_ID",
      partnerToken: "YOUR_PARTNER_TOKEN",
      environment: "staging",
      loginOnInitialize: true,
      providerId: "YOUR_PROVIDER_ID", // Replace with your provider ID
      providerName: "Dr. Jane Smith", // Example only — replace with the provider's full name
      providerOrgId: "YOUR_ORG_ID",
    });
    return new DictationClient({ authManager });
    }, []);
    }
    ```
  </Step>

  <Step title="Wrap the Chart Screen with DictationProvider">
    Pass the shared client into `DictationProvider`. Every `&lt;Dictation&gt;` must be a child of that provider.

    ```tsx theme={"theme":{"light":"github-dark","dark":"material-theme-darker"}}
    import { DictationProvider } from "@suki-sdk/dictation-react";

    function ChartNoteScreen({ client }) {
    return (
    <DictationProvider client={client}>
      <ChartNoteField />
    </DictationProvider>
    );
    }
    ```
  </Step>

  <Step title="Dictate into a Chart Field in In-Field Mode">
    Use a stable `fieldId` that matches your textarea id. Pass a wrapper with real height as `rootElement`. Mount `&lt;Dictation&gt;` only while that field should own the session. Update the field from `onSubmit`.

    ```tsx theme={"theme":{"light":"github-dark","dark":"material-theme-darker"}}
    import { useEffect, useRef, useState } from "react";
    import { Dictation } from "@suki-sdk/dictation-react";

    function ChartNoteField() {
    const dictationRootRef = useRef(null);
    const [dictationRoot, setDictationRoot] = useState(null);
    const [notes, setNotes] = useState("");
    const [dictationActive, setDictationActive] = useState(false);

    // Capture the wrapper DOM node for rootElement (stable height host for the iframe).
    useEffect(() => {
    setDictationRoot(dictationRootRef.current);
    }, []);

    return (
    <>
      <label htmlFor="clinical-notes">Clinical notes</label>
      {/* rootElement should be a wrapper with real height, not the textarea alone */}
      <div
        ref={dictationRootRef}
        id="clinical-notes-dictation-root"
        style={{ position: "relative", minHeight: 120 }}
      >
        <textarea
          id="clinical-notes"
          value={notes}
          onChange={(e) => setNotes(e.target.value)}
          rows={8}
        />
      </div>
      <button type="button" onClick={() => setDictationActive((v) => !v)}>
        {dictationActive ? "Stop" : "Start"}
      </button>
      {dictationActive && dictationRoot && (
        <Dictation
          fieldId="clinical-notes" // Example — must match the textarea id
          mode="in-field"
          rootElement={dictationRoot} // Recommended for in-field layout
          initialText={notes}
          onSubmit={({ text }) => {
            setNotes(text);
            setDictationActive(false);
          }}
          onCancel={() => setDictationActive(false)}
        />
      )}
    </>
    );
    }
    ```
  </Step>
</Steps>

<span id="full-example" />

## Full code example

<div className="doc-tutorial-full-code" data-doc-tutorial-full-code data-active-lang="react">
  <Columns cols={1}>
    <Tile href="#full-example-react">
      <span className="doc-tutorial-tile-lang-icon" data-icon="react" aria-hidden="true" />
    </Tile>
  </Columns>

  <div id="full-example-react" className="doc-tutorial-lang-panel is-active" data-lang-panel="react">
    ```tsx React theme={"theme":{"light":"github-dark","dark":"material-theme-darker"}}
    // Dictation SDK chart field tutorial (React, Beta)
    // Copy this file into a React app, replace YOUR_* placeholders, then render <App />.
    // Flow: DictationClient once → DictationProvider → in-field <Dictation> with rootElement + callbacks

    import { useEffect, useMemo, useRef, useState } from "react";
    import { SukiAuthManager } from "@suki-sdk/core";
    import { DictationClient } from "@suki-sdk/dictation";
    import { DictationProvider, Dictation } from "@suki-sdk/dictation-react";

    function App() {
      return <ChartNoteWithDictation />;
    }

    function ChartNoteWithDictation() {
      // 1) Build auth + client once. Creating a new client per render tears down the iframe.
      const client = useMemo(() => {
        const authManager = new SukiAuthManager({
          partnerId: "YOUR_PARTNER_ID", // Required: from partner onboarding
          partnerToken: "YOUR_PARTNER_TOKEN", // Required
          environment: "staging", // "staging" | "production"
          loginOnInitialize: true,
          autoRegister: false, // Optional (default false); when true, provider fields below are often required
          providerId: "YOUR_PROVIDER_ID", // Replace with your provider ID
          providerName: "Dr. Jane Smith", // Example only — replace with the provider's full name
          providerOrgId: "YOUR_ORG_ID",
        });
        return new DictationClient({ authManager });
      }, []);

      const dictationRootRef = useRef(null);
      const [dictationRoot, setDictationRoot] = useState(null);
      const [notes, setNotes] = useState("");
      const [dictationActive, setDictationActive] = useState(false);

      // 2) Capture the wrapper DOM node so rootElement is ready before Dictation mounts.
      useEffect(() => {
        setDictationRoot(dictationRootRef.current);
      }, []);

      return (
        // 3) Every <Dictation> must sit under DictationProvider with the shared client.
        <DictationProvider client={client}>
          <label htmlFor="clinical-notes">Clinical notes</label>
          {/* 4) Wrapper layout: rootElement needs real width/height for the hosted iframe */}
          <div
            ref={dictationRootRef}
            id="clinical-notes-dictation-root"
            style={{ position: "relative", minHeight: 120 }}
          >
            <textarea
              id="clinical-notes" // Example field id — use any stable id; must match fieldId
              value={notes}
              onChange={(e) => setNotes(e.target.value)}
              rows={8}
            />
          </div>
          <button type="button" onClick={() => setDictationActive((v) => !v)}>
            {dictationActive ? "Stop dictation" : "Start dictation"}
          </button>
          {/* 5) Mount in-field dictation only while this field should own the session */}
          {dictationActive && dictationRoot && (
            <Dictation
              fieldId="clinical-notes" // Example — must match the textarea id above
              mode="in-field"
              rootElement={dictationRoot}
              initialText={notes} // Seed the session with current field text
              onSubmit={({ text }) => {
                // onSubmit is required for a good UX; commit transcript, then unmount
                setNotes(text);
                setDictationActive(false);
              }}
              onCancel={() => setDictationActive(false)}
              onDraft={({ text }) => {
                // Optional: persist unstaged text if the user leaves without submit
                console.log("Draft text:", text);
              }}
            />
          )}
        </DictationProvider>
      );
    }

    export default App;
    ```
  </div>
</div>

<span id="troubleshooting" />

## Troubleshooting

<AccordionGroup>
  <Accordion title="Iframe / CSP Issues">
    Meet [Dictation SDK prerequisites](/dictation-sdk/prerequisites) for allowed hosts.
  </Accordion>

  <Accordion title="UI Tears Down on Typing">
    Reuse one `DictationClient` for the page; recreating it remounts the iframe.
  </Accordion>

  <Accordion title="Beta Packaging">
    Confirm package versions with your Suki contact before production use.
  </Accordion>
</AccordionGroup>

<span id="summary" />

## Summary

In this tutorial, you:

* Created one shared `DictationClient` for the chart screen.
* Wrapped the screen with `DictationProvider`.
* Mounted in-field Dictation so submitted text updates the notes field.

<span id="other-tutorials" />

## Other tutorials

Continue with another end-to-end tutorial.

<div className="hp-io-method-grid tut-hub-card-grid">
  <a className="hp-io-method-card tut-hub-method-card" href="/documentation/tutorials/dictation-websocket-code-example">
    <div className="tut-hub-card-media tut-hub-card-media--blue" aria-hidden="true" />

    <div className="hp-io-method-card-body">
      <span className="hp-wn-badge hp-wn-badge-new">Dictation</span>
      <h3 className="hp-io-method-card-title">Build a Dictation Streaming Client</h3>

      <p className="hp-io-method-card-desc">
        Create a transcription session, stream audio, read partial frames, and print the final transcript.
      </p>

      <div className="hp-io-method-card-meta tut-hub-card-foot" aria-label="15 min, Intermediate">
        <div className="tut-hub-card-foot-meta">
          <span className="hp-io-method-card-meta-time">15 min</span>
          <span className="tut-hub-level">Intermediate</span>
        </div>
      </div>
    </div>
  </a>

  <a className="hp-io-method-card tut-hub-method-card" href="/documentation/tutorials/web-sdk-ambient-react">
    <div className="tut-hub-card-media tut-hub-card-media--blue" aria-hidden="true" />

    <div className="hp-io-method-card-body">
      <span className="hp-wn-badge hp-wn-badge-new">Ambient</span>
      <h3 className="hp-io-method-card-title">Build a Web SDK Ambient Session</h3>

      <p className="hp-io-method-card-desc">
        Authenticate, initialize the Web SDK in React, and mount SukiAssistant for an ambient encounter.
      </p>

      <div className="hp-io-method-card-meta tut-hub-card-foot" aria-label="15 min, Intermediate">
        <div className="tut-hub-card-foot-meta">
          <span className="hp-io-method-card-meta-time">15 min</span>
          <span className="tut-hub-level">Intermediate</span>
        </div>
      </div>
    </div>
  </a>
</div>
