Email Blocks
ContactFilterBuilder
Build audience filters for broadcasts.
ContactFilterBuilder edits a ContactFilterWithAggregator with segment selection,
search, applied filter chips, match mode, clearing, and optional segment saving.
Host apps can inject their own discrete filter definitions for app-specific
filters like products, communities, permissions, tags, or date-based activity.
Every visible string (button/heading/dialog text, aria-labels, the default
"Everyone" segment name) is a prop with an English default, down to the
delete-segment confirmation copy (deleteSegmentDescription, a function of
the segment being deleted).
ContactFilterBuilder
Audience filters for broadcasts.
51 Users
Tag: is customers
Email: contains @example.com
Usage
This is the exact code behind the demo above.
import { useState } from "react";
import {
ContactFilterBuilder,
type ContactFilterDefinition,
type ContactFilterSegment,
type ContactFilterWithAggregator,
} from "@sendlit/email-blocks";
const initialFilter: ContactFilterWithAggregator = {
aggregator: "and",
filters: [
{ name: "tag", condition: "is", value: "customers" },
{ name: "email", condition: "contains", value: "@example.com" },
],
};
const initialContactSegments: ContactFilterSegment[] = [
{
id: "paid-product",
name: "Paid product",
filter: {
aggregator: "and",
filters: [
{ name: "subscription", condition: "is", value: "subscribed" },
],
},
},
{
id: "tagged-customers",
name: "Tagged: customers",
filter: {
aggregator: "and",
filters: [{ name: "tag", condition: "is", value: "customers" }],
},
},
];
const contactFilterDefinitions: ContactFilterDefinition[] = [
{
name: "email",
label: "Email",
conditions: [
{ value: "is", label: "is exactly" },
{ value: "contains", label: "contains" },
{ value: "not_contains", label: "does not contain" },
],
valueInput: {
type: "text",
label: "Email value",
placeholder: "e.g. example.com",
},
},
{
name: "community",
label: "Community",
conditions: [
{ value: "has", label: "member of" },
{ value: "does_not_have", label: "not a member of" },
],
valueInput: {
type: "select",
label: "Community",
placeholder: "Select community",
options: [
{ value: "community-discussion", label: "Discussion Learners" },
{ value: "community-customers", label: "Customers" },
],
},
},
{
name: "lastActive",
label: "Last active",
conditions: [
{ value: "after", label: "after" },
{ value: "before", label: "before" },
{ value: "on", label: "on" },
],
valueInput: {
type: "date",
label: "Last active date",
dateValueFormat: "date",
},
},
{
name: "tag",
label: "Tag",
conditions: [
{ value: "has", label: "has" },
{ value: "does_not_have", label: "does not have" },
],
valueInput: {
type: "select",
label: "Tag",
placeholder: "Select a tag",
options: [
{ value: "customers", label: "customers" },
{ value: "webinar", label: "webinar" },
{ value: "trial", label: "trial" },
],
},
},
{
name: "permission",
label: "Permission",
conditions: [
{ value: "has", label: "has" },
{ value: "does_not_have", label: "does not have" },
],
valueInput: {
type: "select",
label: "Permission",
placeholder: "Select permission",
options: [
{ value: "manage_products", label: "Manage products" },
{ value: "publish_content", label: "Publish content" },
{ value: "buy_products", label: "Buy products" },
{ value: "manage_users", label: "Manage users" },
{ value: "manage_community", label: "Manage community" },
],
},
},
];
function Example() {
const [filter, setFilter] =
useState<ContactFilterWithAggregator>(initialFilter);
const [contactSegments, setContactSegments] = useState<
ContactFilterSegment[]
>(initialContactSegments);
const [selectedSegmentId, setSelectedSegmentId] = useState("");
return (
<ContactFilterBuilder
value={filter}
onChange={setFilter}
filterDefinitions={contactFilterDefinitions}
segments={contactSegments}
selectedSegmentId={selectedSegmentId}
onSegmentSelect={(segment) => setSelectedSegmentId(segment.id)}
onSaveSegment={(name, savedFilter) => {
const newSegment: ContactFilterSegment = {
id: `segment-${Date.now()}`,
name,
filter: savedFilter,
};
setContactSegments((current) => [...current, newSegment]);
setSelectedSegmentId(newSegment.id);
}}
onDeleteSegment={(segment) => {
setContactSegments((current) =>
current.filter((item) => item.id !== segment.id),
);
}}
count={51}
/>
);
}