Integrate Mod Editor

Getting started with React

See a live example (opens in a new tab)

Add the core libraries

yarn add @mod-protocol/react @mod-protocol/react-editor @mod-protocol/miniapp-registry

Add the default UI library, or use your own. The default uses shadcn (opens in a new tab) which uses Tailwind (opens in a new tab) + Radix UI (opens in a new tab) under the hood We've made these packages modular to reduce unnecessary packages, facilitate tree shaking, and allow for flexibility to integrate natively with your UI.

yarn add @mod-protocol/react-ui-shadcn

Mod Editor for Farcaster Cast creation

import * as React from "react";
 
// Core
import {
  Channel,
  getFarcasterChannels,
  getFarcasterMentions,
} from "@mod-protocol/farcaster";
import { CreationMiniApp } from "@mod-protocol/react";
import { useEditor, EditorContent } from "@mod-protocol/react-editor";
import { creationMiniApps } from "@mod-protocol/miniapp-registry";
import {
  Embed,
  ModManifest,
  fetchUrlMetadata,
  handleAddEmbed,
  handleOpenFile,
  handleSetInput,
} from "@mod-protocol/core";
 
// UI implementation
import { createRenderMentionsSuggestionConfig } from "@mod-protocol/react-ui-shadcn/dist/lib/mentions";
import { CreationMiniAppsSearch } from "@mod-protocol/react-ui-shadcn/dist/components/creation-miniapps-search";
import { CastLengthUIIndicator } from "@mod-protocol/react-ui-shadcn/dist/components/cast-length-ui-indicator";
import { ChannelPicker } from "@mod-protocol/react-ui-shadcn/dist/components/channel-picker";
import { EmbedsEditor } from "@mod-protocol/react-ui-shadcn/dist/lib/embeds";
import { Button } from "@mod-protocol/react-ui-shadcn/dist/components/ui/button";
import {
  Popover,
  PopoverContent,
  PopoverTrigger,
} from "@mod-protocol/react-ui-shadcn/dist/components/ui/popover";
import { renderers } from "@mod-protocol/react-ui-shadcn/dist/renderers";
 
// Optionally replace with your API_URL here if you want to self host by running your own instance of https://github.com/mod-protocol/mod/tree/main/examples/api
const API_URL = "https://api.modprotocol.org";
const getResults = getFarcasterMentions(API_URL);
const getChannels = getFarcasterChannels(API_URL);
const getUrlMetadata = fetchUrlMetadata(API_URL);
const onError = (err) => window.alert(err.message);
const onSubmit = async ({
  text,
  embeds,
  channel,
}: {
  text: string;
  embeds: Embed[];
  channel: Channel;
}) => {
  window.alert(
    `This is a demo, and doesn't do anything.\n\nCast text:\n${text}\nEmbeds:\n${embeds
      .map((embed) => (embed as any).url)
      .join(", ")}\nChannel:\n${channel.name}`
  );
 
  return true;
};
 
export default function EditorExample() {
  const {
    editor,
    getText,
    getEmbeds,
    setEmbeds,
    setText,
    setChannel,
    getChannel,
    addEmbed,
    handleSubmit,
  } = useEditor({
    fetchUrlMetadata: getUrlMetadata,
    onError,
    onSubmit,
    linkClassName: "text-blue-600",
    renderMentionsSuggestionConfig: createRenderMentionsSuggestionConfig({
      getResults: getResults,
    }),
  });
 
  const [currentMiniapp, setCurrentMiniapp] =
    React.useState<ModManifest | null>(null);
 
  return (
    <form onSubmit={handleSubmit}>
      <div className="p-2 border-slate-200 rounded-md border">
        <EditorContent
          editor={editor}
          autoFocus
          className="w-full h-full min-h-[200px]"
        />
        <EmbedsEditor embeds={getEmbeds()} setEmbeds={setEmbeds} />
      </div>
      <div className="flex flex-row pt-2 gap-1">
        <ChannelPicker
          getChannels={getChannels}
          onSelect={setChannel}
          value={getChannel()}
        />
        <Popover
          open={!!currentMiniapp}
          onOpenChange={(op: boolean) => {
            if (!op) setCurrentMiniapp(null);
          }}
        >
          <PopoverTrigger></PopoverTrigger>
          <CreationMiniAppsSearch
            miniapps={creationMiniApps}
            onSelect={setCurrentMiniapp}
          />
          <PopoverContent className="w-[400px] ml-2" align="start">
            <div className="space-y-4">
              <h4 className="font-medium leading-none">
                {currentMiniapp?.name}
              </h4>
              <hr />
              <CreationMiniApp
                input={getText()}
                embeds={getEmbeds()}
                api={API_URL}
                variant="creation"
                manifest={currentMiniapp}
                renderers={renderers}
                onOpenFileAction={handleOpenFile}
                onExitAction={() => setCurrentMiniapp(null)}
                onSetInputAction={handleSetInput(setText)}
                onAddEmbedAction={handleAddEmbed(addEmbed)}
              />
            </div>
          </PopoverContent>
        </Popover>
 
        <CastLengthUIIndicator getText={getText} />
        <div className="grow"></div>
        <Button type="submit">Cast</Button>
      </div>
    </form>
  );
}

Embed rendering Mini-apps

"use client";
 
import { renderers } from "@mod-protocol/react-ui-shadcn";
import { RenderEmbed } from "@mod-protocol/react";
import { Embed } from "@mod-protocol/core";
 
export function FarcasterEmbeds(props: { embeds: Array<Embed> }) {
  return (
    <div>
      {props.embeds.map((embed, i) => (
        <RenderEmbed embed={embed} key={i} renderers={renderers} />
      ))}
    </div>
  );
}

Integrating Creation Mini-apps with your own editor

You can integrate Creation Mini-apps with an existing editor, just pass the text and embeds from your editor into the CreationMiniApp component.