# Date Separators

Our `MessageList`/`MessageFlashList` components render an inline date separator above the first message of each day, so everything below a separator belongs to the day it names.

System messages — "X was added to the channel", channel updates, or any message your backend writes with `type: 'system'` — are excluded from this by default. That matters when a system message is the _first_ message of a day: it takes the day boundary and renders nothing, and the regular message below it shares the same day, so that day ends up with no separator at all.

If your channels get system messages at day boundaries, enable `allowDateSeparatorForSystemMessages` on `Channel`:

```tsx
<Channel channel={channel} allowDateSeparatorForSystemMessages>
  <MessageList />
  <MessageComposer />
</Channel>
```

With it on, the separator is rendered above the system message that opens the day, and the regular messages that follow correctly get none — the day is still dated exactly once.

## Hiding separators on selected days

Some applications write a system message to mark something other than a membership change — the start of a support session, for example — and would rather not date a day that holds nothing but those markers.

The separator is a component slot, so returning `null` from it suppresses it for that day. Combine it with `allowDateSeparatorForSystemMessages`: without the flag there is no separator on those days to suppress in the first place.

```tsx
import {
  Channel,
  InlineDateSeparator,
  InlineDateSeparatorProps,
  MessageList,
  usePaginatedMessageListContext,
  WithComponents,
} from "stream-chat-react-native";

const DaysWithMessagesOnly = ({ date }: InlineDateSeparatorProps) => {
  const { hasMore, messages } = usePaginatedMessageListContext();

  if (!date) return null;

  const day = date.toDateString();
  const hasRegularMessage = messages.some(
    (message) =>
      message.type !== "system" && message.created_at.toDateString() === day,
  );

  // The oldest loaded day may still gain messages from the next page, so leave it dated until
  // the history behind it is loaded. Otherwise the separator appears mid-scroll after `loadMore`.
  const dayIsPartiallyLoaded =
    day === messages[0]?.created_at.toDateString() && hasMore;

  if (!hasRegularMessage && !dayIsPartiallyLoaded) return null;

  return <InlineDateSeparator date={date} />;
};

export const Chat = () => (
  <WithComponents overrides={{ InlineDateSeparator: DaysWithMessagesOnly }}>
    <Channel channel={channel} allowDateSeparatorForSystemMessages>
      <MessageList />
    </Channel>
  </WithComponents>
);
```

`hasMore` and `messages` both come from [`PaginatedMessageListContext`](https://getstream.io/chat/docs/sdk/react-native/contexts/paginated-message-list-context/); the same values are available on the channel as `channel.state.messagePagination.hasPrev` and `channel.state.messages` if that suits your structure better.

:::note
`MessageWrapper` wraps whatever the slot renders in a `View` carrying `messageList.inlineDateSeparatorContainer`, whose default includes vertical padding. Returning `null` leaves that padding behind as a gap, so set `inlineDateSeparatorContainer: { paddingVertical: 0 }` in your theme and apply the spacing inside your own component instead.
:::

## Performance

The rule above scans the loaded messages on every separator render. On channels that load a lot of history, cache the result per message list instead — the client hands out a new array whenever the list changes, so a `WeakMap` keyed on it computes once and every separator after that is a lookup:

```tsx
const dayCache = new WeakMap<readonly LocalMessage[], Set<number>>();

const dayKey = (date: Date) =>
  date.getFullYear() * 10000 + date.getMonth() * 100 + date.getDate();

const daysWithRegularMessages = (messages: readonly LocalMessage[]) => {
  let days = dayCache.get(messages);

  if (!days) {
    days = new Set<number>();
    for (const message of messages) {
      if (message.type !== "system") days.add(dayKey(message.created_at));
    }
    dayCache.set(messages, days);
  }

  return days;
};
```

Comparing integer day keys rather than `toDateString()` values is also considerably cheaper, and it matters here because the comparison runs once per loaded message.

Finally, set [`maximumMessageLimit`](https://getstream.io/chat/docs/sdk/react-native/core-components/channel/) on `Channel` to bound how much history stays loaded. It has no default, so a long-lived channel keeps growing and every list update walks all of it.

## Turning separators off entirely

To remove inline date separators altogether, use `hideDateSeparators` on `Channel`. It takes precedence over `allowDateSeparatorForSystemMessages`, and `hideStickyDateHeader` does the same for the floating date header at the top of the list.

---

For the most recent version of this documentation, visit [https://getstream.io/chat/docs/sdk/react-native/guides/date-separators/](https://getstream.io/chat/docs/sdk/react-native/guides/date-separators/).