@pipedream/connect-react v1.3.0
Pipedream Connect-React
This package is actively maintained and is continuing to expand supported capabilities and overall developer experience.
Please join our community or reach out to
connect@pipedream.comwith feedback.
Installation and usage
% npm install --save @pipedream/connect-reactThen use the ComponentFormContainer component in your app (see below for props
and customization).
!NOTE To run the example below, set the following environment variables in
.env.local.
PIPEDREAM_ALLOWED_ORIGINSMake sure this matches the origin of your app, e.g.
# One domain — note the array PIPEDREAM_ALLOWED_ORIGINS=["https://example.com"] # Multiple domains PIPEDREAM_ALLOWED_ORIGINS=["https://example.com", "http://localhost:3000"]
PIPEDREAM_CLIENT_IDCreate a Pipedream API OAuth client and enter its client ID here.
PIPEDREAM_CLIENT_SECRETYour OAuth client secret. This is a secret, and should not be exposed to your frontend.
PIPEDREAM_PROJECT_ID
/* actions.ts */
"use server";
import { createBackendClient } from "@pipedream/sdk/server";
const {
NODE_ENV,
PIPEDREAM_ALLOWED_ORIGINS,
PIPEDREAM_CLIENT_ID,
PIPEDREAM_CLIENT_SECRET,
PIPEDREAM_PROJECT_ID,
} = process.env;
const allowedOrigins = JSON.parse(PIPEDREAM_ALLOWED_ORIGINS || "[]");
const client = createBackendClient({
environment: NODE_ENV,
projectId: PIPEDREAM_PROJECT_ID,
credentials: {
clientId: PIPEDREAM_CLIENT_ID,
clientSecret: PIPEDREAM_CLIENT_SECRET,
},
});
export async function fetchToken(opts: { externalUserId: string }) {
return await client.createConnectToken({
external_user_id: opts.externalUserId,
allowed_origins: PIPEDREAM_ALLOWED_ORIGINS,
});
}
/* page.tsx */
"use client";
import { useState } from "react";
import { createFrontendClient } from "@pipedream/sdk/browser";
import {
ComponentFormContainer,
FrontendClientProvider,
} from "@pipedream/connect-react";
import { fetchToken } from "./actions";
export default function Page() {
// https://pipedream.com/docs/connect/api#external-users
const userId = "my-authed-user-id";
const client = createFrontendClient({
environment: "development",
externalUserId: userId,
tokenCallback: fetchToken,
});
const [configuredProps, setConfiguredProps] = useState({
text: "hello slack!",
});
return (
<>
<div>My application</div>
<FrontendClientProvider client={client}>
<ComponentFormContainer
userId={userId}
componentKey="slack-send-message"
configuredProps={configuredProps}
onUpdateConfiguredProps={setConfiguredProps}
/>
</FrontendClientProvider>
</>
);
}Components and Props
FrontendClientProvider
Necessary wrapper to provide the frontend client to other components.
type Props = {
client: typeof import("@pipedream/sdk/browser").createFrontendClient;
};ComponentFormContainer
Loader component for ComponentForm.
type ComponentFormContainerProps = {
/** action (or trigger) to look up in the [Pipedream Registry](https://github.com/PipedreamHQ/pipedream/tree/master/components) */
componentKey: string;
} & Omit<ComponentFormProps, "component">; // see belowComponentForm
type ComponentFormProps = {
component: typeof import("@pipedream/sdk").V1Component;
/** External user configuring the form */
userId: string;
/** Form configured values */
configuredProps?: Record<string, any>;
/** Filtering configurable props */
propNames?: string[];
/** Shows submit button + callback when clicked */
onSubmit: (ctx: FormContext) => Awaitable<void>;
/** To control and store configured values on form updates, can be used to call actionRun or triggerDeploy */
onUpdateConfiguredProps: (v: Record<string, any>) => void;
/** Hide optional props section */
hideOptionalProps: boolean;
/** SDK response payload. Used in conjunction with enableDebugging to
* show errors in the form. */
sdkResponse: unknown[] | unknown | undefined;
/** Whether to show show errors in the form. Requires sdkErrors to be set. */
enableDebugging?: boolean;
};Customization
Style individual components using the CustomizeProvider and a CustomizationConfig.
<FrontendClientProvider client={client}>
<CustomizeProvider {...customizationConfig}>
<ComponentFormContainer
key="slack-send-message"
configuredProps={configuredProps}
onUpdateConfiguredProps={setConfiguredProps}
/>
</CustomizeProvider>
</FrontendClientProvider>type CustomizationConfig = {
classNames?: CustomClassNamesConfig;
classNamePrefix?: string;
components?: CustomComponentsConfig;
styles?: CustomStylesConfig;
theme?: CustomThemeConfig;
unstyled?: boolean;
};The classNames prop
Not to be confused with the className prop, classNames takes an object with
keys to represent the inner components of a ComponentForm. Each inner
component takes a callback function with the following signature:
<CustomizeProvider
classNames={{
controlInput: ({ prop }) =>
prop.type === "number" ? "border-red-600" : "border-blue-600",
}}
/>Note on CSS specificity
If you're using the classNames API and you're trying to override some base
styles with the same level of specificity, you must ensure that your provided
styles are declared later than the styles from @pipedream/connect-react
(e.g. the link or style tag in the head of your HTML document) in order for them
to take precedence.
The classNamePrefix prop
If you provide the classNamePrefix prop, all inner elements will be given a
className with the provided prefix.
The components prop
You can rewrite individual inner components with the components prop. If you
want to respect other customizations, such as styles, classNames or theme,
use the useCustomize hook to integrate your base styles with the customization
framework.
In this example, we're replacing the default Label inner component with a version
that adds a checkmark to highlight required props.
import type { CSSProperties } from "react";
import type { ConfigurableProp, LabelProps } from "@pipedream/connect-react";
import { useCustomize } from "@pipedream/connect-react";
export function CustomLabel<T extends ConfigurableProp>(props: LabelProps<T>) {
const { text, field } = props;
const { id } = field;
const { getProps, theme } = useCustomize();
const baseStyles: CSSProperties = {
color: theme.colors.neutral90,
fontWeight: 450,
gridArea: "label",
textTransform: "capitalize",
lineHeight: "1.5",
};
const required =
!field.prop.optional && !["alert", "app"].includes(field.prop.type) ? (
field.prop.type == "boolean" ? (
typeof field.value != "undefined"
) : !!field.value ? (
<span
style={{ color: "#12b825", fontSize: "small", marginLeft: "0.5rem" }}
>
{" "}
✓
</span>
) : (
<span
style={{ color: "#d0d0d0", fontSize: "small", marginLeft: "0.5rem" }}
>
{" "}
✓
</span>
)
) : (
""
);
return (
<label htmlFor={id} {...getProps("label", baseStyles, props)}>
{text}
{required}
</label>
);
}Then we apply the custom component using CustomizeProvider.
import { CustomLabel } from "./CustomLabel";
<CustomizeProvider
components={{
Label: CustomLabel,
}}
/>;The styles prop
The recommended way to provide custom styles is to use the styles prop. Each inner
component takes either a CSSProperties object or a callback function with the following
signature:
<CustomizeProvider
styles={{
label: { fontSize: "80%" },
controlInput: (base, { theme }) => ({
...base,
borderTop: 0,
borderLeft: 0,
borderRight: 0,
border: "solid",
borderColor: theme.colors.primary,
backgroundColor: theme.colors.neutral0,
}),
}}
/>Note that when using the callback function, the styles prop has access to theme
customizations as well.
The theme prop
The default styles are derived from a theme object, which can be customized using
the theme prop.
<CustomizeProvider
theme={{
borderRadius: 0,
colors: {
primary: "hsl(200, 100%, 60%)",
primary75: "hsl(200, 100%, 55%)",
primary50: "hsl(200, 100%, 40%)",
primary25: "hsl(200, 100%, 35%)",
danger: "#DE350B",
dangerLight: "#FFBDAD",
neutral0: "hsl(200, 50%, 97%)",
neutral5: "hsl(200, 50%, 95%)",
neutral10: "hsl(200, 50%, 90%)",
neutral20: "hsl(200, 50%, 80%)",
neutral30: "hsl(200, 50%, 70%)",
neutral40: "hsl(200, 50%, 60%)",
neutral50: "hsl(200, 50%, 50%)",
neutral60: "hsl(200, 50%, 40%)",
neutral70: "hsl(200, 50%, 30%)",
neutral80: "hsl(200, 50%, 20%)",
neutral90: "hsl(200, 50%, 10%)",
},
spacing: {
baseUnit: 4,
controlHeight: 10,
menuGutter: 6,
},
}}
/>The unstyled prop
While it is always possible to override default styling, you may prefer to start
from completely unstyled components. Add the unstyled prop to remove all styling.
<CustomizeProvider unstyled={true} />Inner components
The following list shows all of the customizable inner components used in a
ComponentForm.
export type CustomizableProps = {
componentForm: ComponentProps<typeof ComponentForm>;
connectButton: ComponentProps<typeof ControlApp> &
FormFieldContext<ConfigurableProp>;
controlAny: ComponentProps<typeof ControlAny> &
FormFieldContext<ConfigurableProp>;
controlApp: ComponentProps<typeof ControlApp> &
FormFieldContext<ConfigurableProp>;
controlBoolean: ComponentProps<typeof ControlBoolean> &
FormFieldContext<ConfigurableProp>;
controlInput: ComponentProps<typeof ControlInput> &
FormFieldContext<ConfigurableProp>;
controlSubmit: ComponentProps<typeof ControlSubmit>;
description: ComponentProps<typeof Description>;
error: ComponentProps<typeof Errors>;
errors: ComponentProps<typeof Errors>;
field: ComponentProps<typeof Field>;
heading: ComponentProps<typeof ComponentForm>;
label: ComponentProps<typeof Label>;
optionalFields: ComponentProps<typeof ComponentForm>;
optionalFieldButton: ComponentProps<typeof OptionalFieldButton>;
};Internally, @pipedream/connect-react uses react-select to implement complex
dropdown components.
export type ReactSelectComponents = {
controlAppSelect: typeof ControlApp;
controlSelect: typeof ControlSelect;
};Customizing dropdown components is very similar to customizing other inner components
except each dropdown supports deeper customization of elements within react-select
<CustomizeProvider
styles={{
controlSelect: {
control: (base, { theme }) => ({
...base,
borderRadius: 0,
borderColor: theme.colors.primary25,
fontSize: "small",
maxHeight: "36px",
}),
},
}}
/>See React Select for more details on the customization options available.
Hooks
useCustomize— see aboveuseFormContextuseFormFieldContextuseFrontendClient— allows use of provided Pipedream frontendClientuseAccounts— react-query wrapper to list Pipedream connect accounts (for app, external user, etc.)useApp— react-query wrapper to retrieve a Pipedream appuseApps— react-query wrapper to list Pipedream appsuseComponent— react-query wrapper to retrieve a Pipedream component (action or trigger)useComponents— react-query wrapper to list Pipedream components (actions or triggers)
See hooks folder for details.
6 months ago
6 months ago
6 months ago
6 months ago
7 months ago
7 months ago
9 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
11 months ago
11 months ago
11 months ago
11 months ago
11 months ago
11 months ago
12 months ago
12 months ago
12 months ago
12 months ago
12 months ago
12 months ago
12 months ago
12 months ago
12 months ago
12 months ago
12 months ago
1 year ago