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
ReactNode
지정 안 함
ReactNode
지정 안 함
ThreadList.Items
스레드 목록을 순회하며 components로 받은 행 렌더러를 그린다.
Prop
Type
Default
boolean
지정 안 함
(value: { threadListItem: ThreadListItemState; }) => ReactNode
지정 안 함
ThreadListItemsComponentConfig
지정 안 함
ThreadList.LoadMore
이전 대화를 더 불러오는 버튼이다.
Prop
Type
Default
ReactNode
지정 안 함
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
};