Advanced

Advanced

Customizing the theme components

The components used by the X theme allow some simple customization options for common use cases. However you can also have full control over the xeet by building your own Xeet component with the components and features of the theme that you would like to use.

For example, you can build your own xeet component but without the reply button like so:

my-xeet.tsx
import type { Xeet } from 'react-xeet/api'
import {
  type XComponents,
  XeetContainer,
  XeetHeader,
  XeetInReplyTo,
  XeetBody,
  XeetMedia,
  XeetInfo,
  XeetActions,
  enrichXeet,
} from 'react-xeet'
 
type Props = {
  xeet: Xeet
  components?: XComponents
}
 
export const MyXeet = ({ xeet: t, components }: Props) => {
  const xeet = enrichXeet(t)
  return (
    <XeetContainer>
      <XeetHeader xeet={xeet} components={components} />
      {xeet.in_reply_to_status_id_str && <XeetInReplyTo xeet={xeet} />}
      <XeetBody xeet={xeet} />
      {xeet.mediaDetails?.length ? (
        <XeetMedia xeet={xeet} components={components} />
      ) : null}
      <XeetInfo xeet={xeet} />
      <XeetActions xeet={xeet} />
      {/* We're not including the `XeetReplies` component that adds the reply button */}
    </XeetContainer>
  )
}

Then, you can build your own Xeet component that uses the MyXeet component:

xeet.tsx
import { Suspense } from 'react'
import { getXeet } from 'react-xeet/api'
import { type XeetProps, XeetNotFound, XeetSkeleton } from 'react-xeet'
import { MyXeet } from './my-xeet'
 
const XeetContent = async ({ id, components, onError }: XeetProps) => {
  const xeet = id
    ? await getXeet(id).catch((err) => {
        if (onError) {
          onError(err)
        } else {
          console.error(err)
        }
      })
    : undefined
 
  if (!xeet) {
    const NotFound = components?.XeetNotFound || XeetNotFound
    return <NotFound />
  }
 
  return <MyXeet xeet={xeet} components={components} />
}
 
export const Xeet = ({
  fallback = <XeetSkeleton />,
  ...props
}: XeetProps) => (
  <Suspense fallback={fallback}>
    {/* @ts-ignore: Async components are valid in the app directory */}
    <XeetContent {...props} />
  </Suspense>
)

The Xeet component uses Suspense to progressively load the xeet (non-blocking rendering) and to opt-in into streaming if your framework supports it, like Next.js.

XeetContent is an async component that fetches the xeet and passes it to MyXeet. async only works for React Server Components (RSC) (opens in a new tab) so if your framework does not support RSC you can use SWR (opens in a new tab) instead:

xeet.tsx
'use client'
 
import {
  type XeetProps,
  EmbeddedXeet,
  XeetNotFound,
  XeetSkeleton,
  useXeet,
} from 'react-xeet'
 
export const Xeet = ({
  id,
  apiUrl,
  fallback = <XeetSkeleton />,
  components,
  onError,
}: XeetProps) => {
  const { data, error, isLoading } = useXeet(id, apiUrl)
 
  if (isLoading) return fallback
  if (error || !data) {
    const NotFound = components?.XeetNotFound || XeetNotFound
    return <NotFound error={onError ? onError(error) : error} />
  }
 
  return <EmbeddedXeet xeet={data} components={components} />
}