Creation Mods
Creation Mods enable users to use Mods when creating a post, such as Mods for adding Gifs, Images, Videos, Polls or using AI. You can integrate these with or without the Mod Editor, which is a great Farcaster cast creator with batteries included.
You have the choice whether to use our Mod Editor library or not when integrating Creation Mods.
Integrating Creation Mods with your own editor
import { CreationMod } from "@mod-protocol/react";
...
<CreationMod
input={text}
embeds={embeds}
api={API_URL}
variant="creation"
manifest={currentMod}
renderers={renderers}
onOpenFileAction={handleOpenFile}
onExitAction={hideCurrentMod}
onSetInputAction={handleSetInput}
onAddEmbedAction={handleAddEmbed}
/>
if you want to support the user choosing between Mods, you can give them a UI to select a Mod, or use our component.
Full example with the Mod Editor
Add the core libraries
yarn add @mod-protocol/react @mod-protocol/react-editor @mod-protocol/mod-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
Example using Tailwind & Radix UI
import * as React from "react";
// Core
import {
Channel,
getFarcasterChannels,
getFarcasterMentions,
} from "@mod-protocol/farcaster";
import { CreationMod } from "@mod-protocol/react";
import { useEditor, EditorContent } from "@mod-protocol/react-editor";
import { creationMods } from "@mod-protocol/mod-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 { ModsSearch } from "@mod-protocol/react-ui-shadcn/dist/components/creation-mods-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 { MentionList } from "@mod-protocol/react-ui-shadcn/dist/components/mention-list";
import { ChannelList } from "@mod-protocol/react-ui-shadcn/dist/components/channel-list";
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/api";
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",
renderChannelsSuggestionConfig: createRenderMentionsSuggestionConfig({
getResults: (query) => getChannels(query, true),
RenderList: ChannelList,
}),
renderMentionsSuggestionConfig: createRenderMentionsSuggestionConfig({
getResults: getResults,
RenderList: MentionList,
}),
});
const [currentMod, setCurrentMod] = 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={!!currentMod}
onOpenChange={(op: boolean) => {
if (!op) setCurrentMod(null);
}}
>
<PopoverTrigger></PopoverTrigger>
<ModsSearch mods={creationMods} onSelect={setCurrentMod} />
<PopoverContent className="w-[400px] ml-2" align="start">
<div className="space-y-4">
<h4 className="font-medium leading-none">{currentMod?.name}</h4>
<hr />
<CreationMod
input={getText()}
embeds={getEmbeds()}
api={API_URL}
variant="creation"
manifest={currentMod}
renderers={renderers}
onOpenFileAction={handleOpenFile}
onExitAction={() => setCurrentMod(null)}
onSetInputAction={handleSetInput(setText)}
onAddEmbedAction={handleAddEmbed(addEmbed)}
/>
</div>
</PopoverContent>
</Popover>
<CastLengthUIIndicator getText={getText} />
<div className="grow"></div>
<Button type="submit">Cast</Button>
</div>
</form>
);
}
If you're using @mod-protocol/react-ui-shadcn
, follow the configuration steps for @mod-protocol/react-ui-shadcn
Customizing which Mods to support
You can choose to filter or select which Mods are available
Opt out
...
<ModsSearch
mods={creationMods.filter(mod => mod.slug !== "giphy-picker" && !mod.permissions.includes('user.wallet.address'))}
onSelect={setCurrentMod}
/>
Opt in
...
<ModsSearch
mods={creationMods.filter(mod => ['infura-ipfs-upload', 'livepeer-video'].includes(mod.slug))}
onSelect={setCurrentMod}
/>