# Reference lines in chat

> A gutter button that turns lines or a selection into file:line reference badges in a chat composer.

A shadcn/ui block from the [@reui](https://reui.io) registry, listed on [shadcn.exchange](https://shadcn.exchange/r/reui/c-code-block-25).

- Page, with a live preview: https://shadcn.exchange/r/reui/c-code-block-25
- Screenshot: https://abzjhwgwpdpz7ua5.public.blob.vercel-storage.com/shots/reui/c-code-block-25-light-68396b9180b0119e.png
- Registry item: https://reui.io/r/new-york-v4/c-code-block-25.json
- Listed: 2026-10-13, source last changed 2026-10-13

## Install

```bash
npx shadcn@latest add https://reui.io/r/new-york-v4/c-code-block-25.json
```

Run it in a project that has shadcn/ui set up. It adds the files below and installs what they depend on.

## Dependencies

- Registry items: `@reui/badge`, `button`, `card`, `@reui/code-block`

## Files

### `c-code-block-25.tsx`

```tsx
"use client"

import { useState } from "react"
import { Badge } from "@/components/reui/badge"
import {
  CodeBlock,
  CodeBlockHeader,
  CodeBlockLineActions,
  CodeBlockTitle,
  useCodeBlockSelection,
} from "@/components/reui/code-block/code-block"

import { Button } from "@/components/ui/button"
import { Card } from "@/components/ui/card"
import { IconPlaceholder } from "@/app/(create)/components/icon-placeholder"

const FILE = "use-mobile.ts"
const PATH = "src/hooks/use-mobile.ts"

const code = `import { useEffect, useState } from "react"

const MOBILE_BREAKPOINT = 768

export function useIsMobile() {
  const [isMobile, setIsMobile] = useState<boolean>()

  useEffect(() => {
    const mql = window.matchMedia("(max-width: 767px)")
    const onChange = () => {
      setIsMobile(window.innerWidth < MOBILE_BREAKPOINT)
    }
    mql.addEventListener("change", onChange)
    setIsMobile(window.innerWidth < MOBILE_BREAKPOINT)
    return () => mql.removeEventListener("change", onChange)
  }, [])

  return !!isMobile
}`

/*
 * The gutter button turns code into REFERENCES: press + on a line and the
 * composer below gains a file:line badge, or select a range first and one
 * press references every selected line. `useCodeBlockSelection` is what makes
 * the range case possible - the render prop alone only knows its own line.
 */
function AddToChat({
  line,
  onAdd,
}: {
  line: number
  onAdd: (lines: number[]) => void
}) {
  const { selectedLines, clearSelection } = useCodeBlockSelection()
  const batch = selectedLines.length > 0 ? selectedLines : [line]

  return (
    <Button
      type="button"
      size="icon-xs"
      aria-label={
        batch.length > 1
          ? `Reference ${batch.length} selected lines in the chat`
          : `Reference line ${line} in the chat`
      }
      title={
        batch.length > 1
          ? `Reference ${batch.length} lines`
          : `Reference line ${line}`
      }
      onClick={() => {
        onAdd(batch)
        clearSelection()
      }}
      /* Trimmed below the icon-xs rung: the control hangs over the line
         numbers, so a full 24px chip covers the digits it floats above. */
      className="size-5"
    >
      <IconPlaceholder
        lucide="PlusIcon"
        tabler="IconPlus"
        hugeicons="PlusSignIcon"
        phosphor="PlusIcon"
        remixicon="RiAddLine"
      />
    </Button>
  )
}

export function Pattern() {
  const [refs, setRefs] = useState<number[]>([])
  const [highlighted, setHighlighted] = useState<number | null>(null)

  const addLines = (lines: number[]) =>
    setRefs((current) =>
      [...new Set([...current, ...lines])].sort((a, b) => a - b)
    )

  return (
    <div className="flex w-full max-w-2xl flex-col gap-3">
      <CodeBlock
        code={code}
        language="typescript"
        showLineNumbers
        selectable
        highlightedLines={highlighted === null ? undefined : [highlighted]}
      >
        <CodeBlockHeader>
          <CodeBlockTitle>{FILE}</CodeBlockTitle>
          <span className="text-muted-foreground ml-auto text-xs">
            Press + on a line to reference it
          </span>
        </CodeBlockHeader>

        <CodeBlockLineActions side="gutter">
          {({ line }) => <AddToChat line={line} onAdd={addLines} />}
        </CodeBlockLineActions>
      </CodeBlock>

      {/* The composer the references land in. A Card, not a hand-rolled
          bordered div, so its radius resolves per style. */}
      <Card size="sm" className="gap-2 p-3">
        <div className="flex flex-wrap items-center gap-1.5">
          {refs.length === 0 ? (
            <span className="text-muted-foreground px-1 text-sm">
              Ask about {FILE}...
            </span>
          ) : (
            refs.map((line) => (
              <Badge
                key={line}
                variant={highlighted === line ? "primary-light" : "outline"}
                title={`${PATH}:${line}`}
                className="gap-0.5 font-mono"
              >
                {/* Two actions per chip, so two real buttons: the label
                    highlights the referenced line in the block above through
                    `highlightedLines`, the X removes the reference. */}
                <Button
                  variant="ghost"
                  size="xs"
                  aria-pressed={highlighted === line}
                  aria-label={`Highlight line ${line} in the code`}
                  onClick={() =>
                    setHighlighted((current) =>
                      current === line ? null : line
                    )
                  }
                  className="h-auto p-0 font-mono hover:bg-transparent"
                >
                  {FILE}:{line}
                </Button>
                <Button
                  variant="ghost"
                  size="icon-xs"
                  aria-label={`Remove the reference to line ${line}`}
                  onClick={() => {
                    setRefs((current) =>
                      current.filter((value) => value !== line)
                    )
                    setHighlighted((current) =>
                      current === line ? null : current
                    )
                  }}
                  className="size-4 hover:bg-transparent [&_svg]:size-2.5"
                >
                  <IconPlaceholder
                    lucide="XIcon"
                    tabler="IconX"
                    hugeicons="Cancel01Icon"
                    phosphor="XIcon"
                    remixicon="RiCloseLine"
                  />
                </Button>
              </Badge>
            ))
          )}
        </div>
        <div className="flex items-center gap-2">
          <span className="text-muted-foreground text-xs">
            {refs.length > 0
              ? `${refs.length} ${refs.length === 1 ? "line" : "lines"} in context`
              : "No context yet"}
          </span>
          <Button size="xs" className="ml-auto" disabled={!refs.length}>
            Ask AI
          </Button>
        </div>
      </Card>
    </div>
  )
}
```
