ThreadList

Table & List

사이드바에 스레드 목록을 보여주는 DS Sidebar 합성 목록.

Usage

사이드바에서 대화 목록을 만들고 전환·보관·삭제를 지원할 때 사용한다.

import

import

import { ThreadList } from '@mildang/design-system/unofficial/Chat';

ThreadList 하나만 가져오면 ThreadList.New · ThreadList.Items · ThreadList.LoadMore 를 그 아래에서 쓸 수 있다.

예제를 복사해 쓸 때 필요한 준비

외부 패키지를 따로 설치한다. DS 의 전이 의존성에 기대지 않는다.

Anatomy

tsx

import { ThreadList } from '@mildang/design-system/unofficial/Chat';

export default function Example() {
  return (
    <ThreadList.New />
    <ThreadList.Items />
    <ThreadList.LoadMore />
  );
}

부품

필수 여부

반복

위치

ThreadList.New

필수

ThreadList.Items

필수

ThreadList.LoadMore

선택

  • Sidebar.Provider 하위(Sidebar.Root > Sidebar.Content > Sidebar.Group)에 배치한다.
  • Items의 components에는 ThreadListItem을 넘겨 각 행을 렌더한다.

API Reference

ThreadList.New

새 스레드를 시작하는 버튼이다.

Prop

Type

Default

children

ReactNode

지정 안 함

icon

ReactNode

지정 안 함

ThreadList.Items

스레드 목록을 순회하며 components로 받은 행 렌더러를 그린다.

Prop

Type

Default

archived

boolean

지정 안 함

children

(value: { threadListItem: ThreadListItemState; }) => ReactNode

지정 안 함

components

ThreadListItemsComponentConfig

지정 안 함

ThreadList.LoadMore

이전 대화를 더 불러오는 버튼이다.

Prop

Type

Default

children

ReactNode

지정 안 함

icon

ReactNode

지정 안 함

같은 패밀리

@mildang/design-system/unofficial/Chat 에서 같이 내보내는 컴포넌트다.

예제

기본 사용

새 채팅 버튼과 스레드 목록을 함께 조립한 기본 형태다.

코드

// ThreadListDefault.example.tsx
import { Sidebar } from "@mildang/design-system/Sidebar";
import { ThreadList, ThreadListItem } from "@mildang/design-system/unofficial/Chat";

// ../../examples/mockRuntime.tsx
import {
  AssistantRuntimeProvider,
  useLocalRuntime,
  useRemoteThreadListRuntime
} from "@assistant-ui/react";
import { useState } from "react";
var mockAdapter = {
  async run() {
    return {
      content: [{ type: "text", text: "안녕하세요! 무엇을 도와드릴까요? (mock 응답)" }]
    };
  }
};
var seededThreads = [
  {
    status: "regular",
    remoteId: "thread-1",
    title: "이차방정식 근 구하는 법",
    lastMessageAt: /* @__PURE__ */ new Date("2026-08-05T15:24:00")
  },
  {
    status: "regular",
    remoteId: "thread-2",
    title: "영어 지문 요약해줘",
    lastMessageAt: /* @__PURE__ */ new Date("2026-08-05T11:02:00")
  },
  {
    status: "regular",
    remoteId: "thread-3",
    title: "오늘 학습 계획 짜기",
    lastMessageAt: /* @__PURE__ */ new Date("2026-08-04T20:10:00")
  }
];
var mockThreadListAdapter = {
  async list() {
    return { threads: seededThreads };
  },
  async rename() {
  },
  async archive() {
  },
  async unarchive() {
  },
  async delete() {
  },
  async initialize(threadId) {
    return { remoteId: threadId, externalId: void 0 };
  },
  async fetch(threadId) {
    return seededThreads.find((thread) => thread.remoteId === threadId) ?? {
      status: "regular",
      remoteId: threadId
    };
  },
  // 예제에서는 자동 제목 생성이 발생하지 않으므로(모두 title 보유) 무한 pending으로 안전하게 둔다.
  generateTitle: (() => new Promise(() => {
  }))
};
function MockThreadListRuntimeProvider({ children }) {
  const [threadId, setThreadId] = useState("thread-1");
  const runtime = useRemoteThreadListRuntime({
    runtimeHook: () => useLocalRuntime(mockAdapter),
    adapter: mockThreadListAdapter,
    threadId,
    onThreadIdChange: setThreadId
  });
  return <AssistantRuntimeProvider runtime={runtime}>{children}</AssistantRuntimeProvider>;
}

// ThreadListDefault.example.tsx
var SidebarShell = ({ children }) => <MockThreadListRuntimeProvider>
    <Sidebar.Provider>
      <Sidebar.Root>
        <Sidebar.Content>
          <Sidebar.Group.Root>
            <Sidebar.Group.Label>대화</Sidebar.Group.Label>
            {children}
          </Sidebar.Group.Root>
        </Sidebar.Content>
      </Sidebar.Root>
    </Sidebar.Provider>
  </MockThreadListRuntimeProvider>;
var Item = () => <ThreadListItem>
    <ThreadListItem.Trigger>
      <ThreadListItem.Title />
    </ThreadListItem.Trigger>
    <ThreadListItem.More />
  </ThreadListItem>;
var ThreadListDefaultExample = () => <SidebarShell>
    <ThreadList>
      <ThreadList.New />
      <ThreadList.Items components={{ ThreadListItem: Item }} />
    </ThreadList>
  </SidebarShell>;
var ThreadListDefault_example_default = ThreadListDefaultExample;
export {
  ThreadListDefault_example_default as default
};

행 액션

각 행에 보관·삭제 버튼을 함께 둔 예시다.

코드

// ThreadListWithItemActions.example.tsx
import { Sidebar } from "@mildang/design-system/Sidebar";
import { ThreadList, ThreadListItem } from "@mildang/design-system/unofficial/Chat";

// ../../examples/mockRuntime.tsx
import {
  AssistantRuntimeProvider,
  useLocalRuntime,
  useRemoteThreadListRuntime
} from "@assistant-ui/react";
import { useState } from "react";
var mockAdapter = {
  async run() {
    return {
      content: [{ type: "text", text: "안녕하세요! 무엇을 도와드릴까요? (mock 응답)" }]
    };
  }
};
var seededThreads = [
  {
    status: "regular",
    remoteId: "thread-1",
    title: "이차방정식 근 구하는 법",
    lastMessageAt: /* @__PURE__ */ new Date("2026-08-05T15:24:00")
  },
  {
    status: "regular",
    remoteId: "thread-2",
    title: "영어 지문 요약해줘",
    lastMessageAt: /* @__PURE__ */ new Date("2026-08-05T11:02:00")
  },
  {
    status: "regular",
    remoteId: "thread-3",
    title: "오늘 학습 계획 짜기",
    lastMessageAt: /* @__PURE__ */ new Date("2026-08-04T20:10:00")
  }
];
var mockThreadListAdapter = {
  async list() {
    return { threads: seededThreads };
  },
  async rename() {
  },
  async archive() {
  },
  async unarchive() {
  },
  async delete() {
  },
  async initialize(threadId) {
    return { remoteId: threadId, externalId: void 0 };
  },
  async fetch(threadId) {
    return seededThreads.find((thread) => thread.remoteId === threadId) ?? {
      status: "regular",
      remoteId: threadId
    };
  },
  // 예제에서는 자동 제목 생성이 발생하지 않으므로(모두 title 보유) 무한 pending으로 안전하게 둔다.
  generateTitle: (() => new Promise(() => {
  }))
};
function MockThreadListRuntimeProvider({ children }) {
  const [threadId, setThreadId] = useState("thread-1");
  const runtime = useRemoteThreadListRuntime({
    runtimeHook: () => useLocalRuntime(mockAdapter),
    adapter: mockThreadListAdapter,
    threadId,
    onThreadIdChange: setThreadId
  });
  return <AssistantRuntimeProvider runtime={runtime}>{children}</AssistantRuntimeProvider>;
}

// ThreadListWithItemActions.example.tsx
var SidebarShell = ({ children }) => <MockThreadListRuntimeProvider>
    <Sidebar.Provider>
      <Sidebar.Root>
        <Sidebar.Content>
          <Sidebar.Group.Root>
            <Sidebar.Group.Label>대화</Sidebar.Group.Label>
            {children}
          </Sidebar.Group.Root>
        </Sidebar.Content>
      </Sidebar.Root>
    </Sidebar.Provider>
  </MockThreadListRuntimeProvider>;
var ItemWithActions = () => <ThreadListItem>
    <ThreadListItem.Trigger>
      <ThreadListItem.Title />
    </ThreadListItem.Trigger>
    <ThreadListItem.Archive />
    <ThreadListItem.Delete />
  </ThreadListItem>;
var ThreadListWithItemActionsExample = () => <SidebarShell>
    <ThreadList>
      <ThreadList.New>새 대화 시작</ThreadList.New>
      <ThreadList.Items components={{ ThreadListItem: ItemWithActions }} />
    </ThreadList>
  </SidebarShell>;
var ThreadListWithItemActions_example_default = ThreadListWithItemActionsExample;
export {
  ThreadListWithItemActions_example_default as default
};

더 보기 메뉴 고급 조립

더 보기 메뉴 슬롯을 직접 조립해 커스텀 항목을 추가한 예시다.

코드

// ThreadListMoreAdvanced.example.tsx
import { Sidebar } from "@mildang/design-system/Sidebar";
import { ThreadList, ThreadListItem } from "@mildang/design-system/unofficial/Chat";

// ../../examples/mockRuntime.tsx
import {
  AssistantRuntimeProvider,
  useLocalRuntime,
  useRemoteThreadListRuntime
} from "@assistant-ui/react";
import { useState } from "react";
var mockAdapter = {
  async run() {
    return {
      content: [{ type: "text", text: "안녕하세요! 무엇을 도와드릴까요? (mock 응답)" }]
    };
  }
};
var seededThreads = [
  {
    status: "regular",
    remoteId: "thread-1",
    title: "이차방정식 근 구하는 법",
    lastMessageAt: /* @__PURE__ */ new Date("2026-08-05T15:24:00")
  },
  {
    status: "regular",
    remoteId: "thread-2",
    title: "영어 지문 요약해줘",
    lastMessageAt: /* @__PURE__ */ new Date("2026-08-05T11:02:00")
  },
  {
    status: "regular",
    remoteId: "thread-3",
    title: "오늘 학습 계획 짜기",
    lastMessageAt: /* @__PURE__ */ new Date("2026-08-04T20:10:00")
  }
];
var mockThreadListAdapter = {
  async list() {
    return { threads: seededThreads };
  },
  async rename() {
  },
  async archive() {
  },
  async unarchive() {
  },
  async delete() {
  },
  async initialize(threadId) {
    return { remoteId: threadId, externalId: void 0 };
  },
  async fetch(threadId) {
    return seededThreads.find((thread) => thread.remoteId === threadId) ?? {
      status: "regular",
      remoteId: threadId
    };
  },
  // 예제에서는 자동 제목 생성이 발생하지 않으므로(모두 title 보유) 무한 pending으로 안전하게 둔다.
  generateTitle: (() => new Promise(() => {
  }))
};
function MockThreadListRuntimeProvider({ children }) {
  const [threadId, setThreadId] = useState("thread-1");
  const runtime = useRemoteThreadListRuntime({
    runtimeHook: () => useLocalRuntime(mockAdapter),
    adapter: mockThreadListAdapter,
    threadId,
    onThreadIdChange: setThreadId
  });
  return <AssistantRuntimeProvider runtime={runtime}>{children}</AssistantRuntimeProvider>;
}

// ThreadListMoreAdvanced.example.tsx
var SidebarShell = ({ children }) => <MockThreadListRuntimeProvider>
    <Sidebar.Provider>
      <Sidebar.Root>
        <Sidebar.Content>
          <Sidebar.Group.Root>
            <Sidebar.Group.Label>대화</Sidebar.Group.Label>
            {children}
          </Sidebar.Group.Root>
        </Sidebar.Content>
      </Sidebar.Root>
    </Sidebar.Provider>
  </MockThreadListRuntimeProvider>;
var ItemWithCustomMore = () => <ThreadListItem>
    <ThreadListItem.Trigger>
      <ThreadListItem.Title />
    </ThreadListItem.Trigger>
    <ThreadListItem.More.Root sharedFocusGroup>
      <ThreadListItem.More.Trigger />
      <ThreadListItem.More.Content side="right" align="start">
        <ThreadListItem.More.Item>이름 바꾸기</ThreadListItem.More.Item>
        <ThreadListItem.More.Separator />
        <ThreadListItem.More.Item>사용자 정의 작업</ThreadListItem.More.Item>
      </ThreadListItem.More.Content>
    </ThreadListItem.More.Root>
  </ThreadListItem>;
var ThreadListMoreAdvancedExample = () => <SidebarShell>
    <ThreadList>
      <ThreadList.New />
      <ThreadList.Items components={{ ThreadListItem: ItemWithCustomMore }} />
    </ThreadList>
  </SidebarShell>;
var ThreadListMoreAdvanced_example_default = ThreadListMoreAdvancedExample;
export {
  ThreadListMoreAdvanced_example_default as default
};