# P composer 1

> Composer with people, teams, and slash suggestions

A shadcn/ui block from the [@coss](https://coss.com/ui) registry, listed on [shadcn.exchange](https://shadcn.exchange/r/coss/p-composer-1).

- Page, with a live preview: https://shadcn.exchange/r/coss/p-composer-1
- Screenshot: https://abzjhwgwpdpz7ua5.public.blob.vercel-storage.com/shots/coss/p-composer-1-light-48e51092e157b8cd.png
- Registry item: https://coss.com/ui/r/p-composer-1.json
- Categories: composer, text editor
- Listed: 2026-10-13, source last changed 2026-10-13

## Install

```bash
npx shadcn@latest add https://coss.com/ui/r/p-composer-1.json
```

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

## Dependencies

- npm packages: `lucide-react`
- Registry items: `@coss/button`, `@coss/composer`

## Files

### `registry/default/particles/p-composer-1.tsx`

```tsx
"use client";

import {
  ArrowUpIcon,
  SparklesIcon,
  UserRoundIcon,
  UsersRoundIcon,
} from "lucide-react";
import { useState } from "react";
import { Button } from "@/registry/default/ui/button";
import {
  Composer,
  ComposerActionGroup,
  ComposerActions,
  ComposerInput,
  ComposerItem,
  type ComposerItemValue,
  ComposerList,
  ComposerPopup,
  ComposerSubmit,
  type ComposerTrigger,
  type ComposerValue,
  getComposerText,
} from "@/registry/default/ui/composer";

const people = [
  { id: "alex", label: "Alex Morgan", kind: "person" },
  { id: "sam", label: "Sam Rivera", kind: "person" },
];

const teams = [
  { id: "design", label: "Design team", kind: "team" },
  { id: "engineering", label: "Engineering team", kind: "team" },
];

const actions = [
  { id: "summarize", label: "Summarize", type: "action" as const },
  { id: "rewrite", label: "Rewrite", type: "action" as const },
];

const triggers: ComposerTrigger[] = [
  { character: "@", items: [...people, ...teams] },
  { character: "/", items: actions },
];

export default function Particle() {
  const [value, setValue] = useState<ComposerValue>([]);
  const [notice, setNotice] = useState("");

  return (
    <div className="flex w-full max-w-xl flex-col gap-3">
      <Composer
        value={value}
        onValueChange={setValue}
        triggers={triggers}
        onSubmit={(draft) => {
          setNotice(`Sent: ${getComposerText(draft)}`);
          setValue([]);
        }}
      >
        <ComposerInput aria-label="Message" placeholder="Write a message…" />
        <ComposerPopup>
          <ComposerList>
            {(item: ComposerItemValue) => (
              <ComposerItem key={item.id} value={item}>
                {item.type === "action" ? (
                  <SparklesIcon
                    aria-hidden="true"
                    className="size-4 opacity-80"
                  />
                ) : item.kind === "team" ? (
                  <UsersRoundIcon
                    aria-hidden="true"
                    className="size-4 opacity-80"
                  />
                ) : (
                  <UserRoundIcon
                    aria-hidden="true"
                    className="size-4 opacity-80"
                  />
                )}
                <span className="truncate">{item.label}</span>
              </ComposerItem>
            )}
          </ComposerList>
        </ComposerPopup>
        <ComposerActions>
          <ComposerActionGroup align="end">
            <ComposerSubmit
              render={
                <Button aria-label="Send message" size="icon">
                  <ArrowUpIcon aria-hidden="true" />
                </Button>
              }
            />
          </ComposerActionGroup>
        </ComposerActions>
      </Composer>
      <p className="min-h-5 text-muted-foreground text-sm" role="status">
        {notice}
      </p>
    </div>
  );
}
```
