From fd0918833ff3d8388144c559441abe043239d5c2 Mon Sep 17 00:00:00 2001
From: Danny White <3104761+dnywh@users.noreply.github.com>
Date: Mon, 28 Sep 2026 10:59:27 +1000
Subject: [PATCH] feat(studio): refine pipeline creation copy (#50751)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
## Problem
The pipeline creation sheet uses generic examples, inconsistent
destination terminology, and Advanced settings copy that does not
explain what is being overridden. Its Docs button also sends most
destinations to the general Pipelines guide.
This PR is stacked on #50719 so the shared copy builds on the focused
Snowflake field improvements.
## Solution
Adds destination-specific pipeline-name examples and field descriptions,
clarifies destination summaries and Advanced settings, uses
human-readable ClickHouse engine names, shortens the primary action to
**Start pipeline**, and links the Docs button to the selected
destination guide.
## Review instructions
1. Open **[Database
Replication](https://studio-staging-git-dnywh-studiorefine-pipeline-1eaf59-supabase.vercel.app/dashboard/project/_/database/replication)**
and click **Add pipeline**.
2. Switch between destination types and confirm the pipeline-name
example, destination summary, and field descriptions update
appropriately.
3. Open **Advanced settings** and confirm the batch wait-time default is
shown in the description while the input placeholder is `10000`.
4. Select each supported destination and confirm **Docs** opens its
matching destination guide.
5. Select ClickHouse and confirm the engine choices read
**ReplacingMergeTree** and **MergeTree**.
## Checklist
- [x] I have read
[CONTRIBUTING.md](https://github.com/supabase/supabase/blob/master/CONTRIBUTING.md)
- [x] If I wrote a new docs topic or edited an existing topic, I used
the `/write-the-docs` or `/edit-the-docs` skill, which references
[WORD_LIST](https://github.com/supabase/supabase/blob/master/apps/docs/WORD_LIST.md)
and the docs
[CONTRIBUTING](https://github.com/supabase/supabase/blob/master/apps/docs/CONTRIBUTING.md)
guide
## Summary by CodeRabbit
* **Updates**
* Clarified replication settings, destination field descriptions, and
batch wait-time guidance.
* Added destination-specific pipeline name placeholders and updated the
labels for ClickHouse engine options.
* Clarified ClickHouse password guidance for new destinations.
* Updated new-pipeline buttons to say “Start pipeline” or “Start
pipeline anyway.”
* Added destination-specific documentation links for BigQuery,
ClickHouse, DuckLake, and Snowflake.
---
.../DestinationForm/AdvancedSettings.tsx | 6 +-
.../AnalyticsBucket/Fields.tsx | 12 ++-
.../DestinationForm/BigQuery/Fields.tsx | 12 ++-
.../DestinationForm/ClickHouse/Fields.tsx | 32 +++++---
.../DestinationFormFieldCopy.ts | 81 +++++++++++++++++++
.../DestinationForm/DestinationNameInput.tsx | 19 ++++-
.../DestinationForm/DuckLake/Fields.tsx | 58 ++++++-------
.../DestinationForm/Snowflake/Fields.tsx | 17 ++--
.../DestinationForm/index.tsx | 6 +-
.../DestinationPanel/DestinationPanel.tsx | 16 +++-
.../DestinationTypeSelection.tsx | 11 ++-
11 files changed, 197 insertions(+), 73 deletions(-)
create mode 100644 apps/studio/components/interfaces/Database/Replication/DestinationPanel/DestinationForm/DestinationFormFieldCopy.ts
diff --git a/apps/studio/components/interfaces/Database/Replication/DestinationPanel/DestinationForm/AdvancedSettings.tsx b/apps/studio/components/interfaces/Database/Replication/DestinationPanel/DestinationForm/AdvancedSettings.tsx
index 40eac139cf7..4c706a8ca98 100644
--- a/apps/studio/components/interfaces/Database/Replication/DestinationPanel/DestinationForm/AdvancedSettings.tsx
+++ b/apps/studio/components/interfaces/Database/Replication/DestinationPanel/DestinationForm/AdvancedSettings.tsx
@@ -55,7 +55,7 @@ export const AdvancedSettings = ({
Advanced settings
- Optional settings to control the pipeline in more depth
+ Customize how the pipeline syncs and replicates data.
@@ -67,7 +67,7 @@ export const AdvancedSettings = ({
@@ -78,7 +78,7 @@ export const AdvancedSettings = ({
step={1}
value={field.value ?? ''}
onChange={handleNumberChange(field)}
- placeholder={`Default: ${DEFAULT_MAX_FILL_MS}`}
+ placeholder={String(DEFAULT_MAX_FILL_MS)}
/>
milliseconds
diff --git a/apps/studio/components/interfaces/Database/Replication/DestinationPanel/DestinationForm/AnalyticsBucket/Fields.tsx b/apps/studio/components/interfaces/Database/Replication/DestinationPanel/DestinationForm/AnalyticsBucket/Fields.tsx
index 5e70348f9eb..2d7f789dfd8 100644
--- a/apps/studio/components/interfaces/Database/Replication/DestinationPanel/DestinationForm/AnalyticsBucket/Fields.tsx
+++ b/apps/studio/components/interfaces/Database/Replication/DestinationPanel/DestinationForm/AnalyticsBucket/Fields.tsx
@@ -25,6 +25,10 @@ import {
STORED_SECRET_PLACEHOLDER,
} from '../DestinationForm.constants'
import type { DestinationPanelSchemaType } from '../DestinationForm.schema'
+import {
+ ANALYTICS_BUCKET_BUCKET_FIELD_COPY,
+ ANALYTICS_BUCKET_NAMESPACE_FIELD_COPY,
+} from '../DestinationFormFieldCopy'
import {
isMetadataListErrorVisible,
isMetadataListLoading,
@@ -155,9 +159,9 @@ export const AnalyticsBucketFields = ({
name="warehouseName"
render={({ field }) => (
(
(
@@ -75,9 +79,9 @@ export const BigQueryFields = ({
name="datasetId"
render={({ field }) => (
diff --git a/apps/studio/components/interfaces/Database/Replication/DestinationPanel/DestinationForm/ClickHouse/Fields.tsx b/apps/studio/components/interfaces/Database/Replication/DestinationPanel/DestinationForm/ClickHouse/Fields.tsx
index decfb4b2da5..e4bd1f3988f 100644
--- a/apps/studio/components/interfaces/Database/Replication/DestinationPanel/DestinationForm/ClickHouse/Fields.tsx
+++ b/apps/studio/components/interfaces/Database/Replication/DestinationPanel/DestinationForm/ClickHouse/Fields.tsx
@@ -16,6 +16,11 @@ import { FormItemLayout } from 'ui-patterns/form/FormItemLayout/FormItemLayout'
import { STORED_SECRET_PLACEHOLDER } from '../DestinationForm.constants'
import type { DestinationPanelSchemaType } from '../DestinationForm.schema'
+import {
+ CLICKHOUSE_DATABASE_FIELD_COPY,
+ CLICKHOUSE_ENGINE_FIELD_COPY,
+ CLICKHOUSE_URL_FIELD_COPY,
+} from '../DestinationFormFieldCopy'
export const ClickHouseFields = ({
form,
@@ -37,8 +42,8 @@ export const ClickHouseFields = ({
render={({ field }) => (
@@ -74,17 +79,18 @@ export const ClickHouseFields = ({
field.onChange(event.target.value)}
actions={
@@ -109,8 +115,8 @@ export const ClickHouseFields = ({
render={({ field }) => (
@@ -125,18 +131,20 @@ export const ClickHouseFields = ({
render={({ field }) => (
- {field.value ?? 'replacing_merge_tree'}
+
+ {field.value === 'merge_tree' ? 'MergeTree' : 'ReplacingMergeTree'}
+
- replacing_merge_tree
- merge_tree
+ ReplacingMergeTree
+ MergeTree
diff --git a/apps/studio/components/interfaces/Database/Replication/DestinationPanel/DestinationForm/DestinationFormFieldCopy.ts b/apps/studio/components/interfaces/Database/Replication/DestinationPanel/DestinationForm/DestinationFormFieldCopy.ts
new file mode 100644
index 00000000000..a4e144d3594
--- /dev/null
+++ b/apps/studio/components/interfaces/Database/Replication/DestinationPanel/DestinationForm/DestinationFormFieldCopy.ts
@@ -0,0 +1,81 @@
+export const PIPELINE_NAME_FIELD_COPY = {
+ label: 'Pipeline name',
+ description: 'Used to identify this pipeline in Supabase.',
+} as const
+
+export const BIGQUERY_PROJECT_ID_FIELD_COPY = {
+ label: 'Project ID',
+ description: 'The Google Cloud project ID where data will be sent.',
+} as const
+
+export const BIGQUERY_DATASET_ID_FIELD_COPY = {
+ label: 'Dataset ID',
+ description: 'The BigQuery dataset where replicated tables will be created.',
+} as const
+
+export const ANALYTICS_BUCKET_BUCKET_FIELD_COPY = {
+ label: 'Bucket',
+ description: 'The Analytics Bucket where data will be stored.',
+} as const
+
+export const ANALYTICS_BUCKET_NAMESPACE_FIELD_COPY = {
+ label: 'Namespace',
+ description: 'The namespace within the bucket where tables will be organized.',
+} as const
+
+export const DUCKLAKE_CATALOG_PROJECT_FIELD_COPY = {
+ label: 'Catalog project',
+ description:
+ "Pipelines connects to this project's Postgres instance to store the DuckLake catalog.",
+} as const
+
+export const DUCKLAKE_STORAGE_PROJECT_FIELD_COPY = {
+ label: 'Storage project',
+ description: 'The project whose object storage holds the DuckLake data files.',
+} as const
+
+export const DUCKLAKE_BUCKET_FIELD_COPY = {
+ label: 'Bucket',
+ description: 'The bucket in which DuckLake data files will be stored.',
+} as const
+
+export const DUCKLAKE_CATALOG_URL_FIELD_COPY = {
+ label: 'Catalog URL',
+ createDescription: 'A Postgres connection string for the DuckLake catalog.',
+ editDescription: 'Stored catalog URL is hidden. Enter a new URL to replace it.',
+} as const
+
+export const DUCKLAKE_DATA_PATH_FIELD_COPY = {
+ label: 'Data path',
+ description: 'An S3 path where DuckLake data files will be written.',
+} as const
+
+export const SNOWFLAKE_ACCOUNT_ID_FIELD_COPY = {
+ label: 'Account ID',
+ description: 'Snowflake organization and account identifiers joined with a hyphen.',
+} as const
+
+export const SNOWFLAKE_DATABASE_FIELD_COPY = {
+ label: 'Database',
+ description: 'Snowflake database where replicated tables are created.',
+} as const
+
+export const SNOWFLAKE_SCHEMA_FIELD_COPY = {
+ label: 'Schema',
+ description: 'An empty Snowflake schema where replicated tables are created.',
+} as const
+
+export const CLICKHOUSE_URL_FIELD_COPY = {
+ label: 'HTTPS URL',
+ description: 'The HTTPS endpoint for your ClickHouse server, including port.',
+} as const
+
+export const CLICKHOUSE_DATABASE_FIELD_COPY = {
+ label: 'Database',
+ description: 'The ClickHouse database where replicated tables will be created.',
+} as const
+
+export const CLICKHOUSE_ENGINE_FIELD_COPY = {
+ label: 'Table engine',
+ description: 'Defaults to ReplacingMergeTree.',
+} as const
diff --git a/apps/studio/components/interfaces/Database/Replication/DestinationPanel/DestinationForm/DestinationNameInput.tsx b/apps/studio/components/interfaces/Database/Replication/DestinationPanel/DestinationForm/DestinationNameInput.tsx
index af5789b8934..54ff10e3a9f 100644
--- a/apps/studio/components/interfaces/Database/Replication/DestinationPanel/DestinationForm/DestinationNameInput.tsx
+++ b/apps/studio/components/interfaces/Database/Replication/DestinationPanel/DestinationForm/DestinationNameInput.tsx
@@ -2,28 +2,39 @@ import type { UseFormReturn } from 'react-hook-form'
import { FormControl, FormField, Input } from 'ui'
import { FormItemLayout } from 'ui-patterns/form/FormItemLayout/FormItemLayout'
+import type { DestinationType } from '../DestinationPanel.types'
import type { DestinationPanelSchemaType } from './DestinationForm.schema'
+import { PIPELINE_NAME_FIELD_COPY } from './DestinationFormFieldCopy'
+
+const PIPELINE_NAME_PLACEHOLDERS: Record = {
+ BigQuery: 'Analytics Pipeline',
+ 'Analytics Bucket': 'Iceberg Pipeline',
+ DuckLake: 'Lakehouse Pipeline',
+ Snowflake: 'Data Warehouse Pipeline',
+ ClickHouse: 'Live Analytics Pipeline',
+}
type DestinationNameInputProps = {
form: UseFormReturn
+ destinationType: DestinationType
}
-export const DestinationNameInput = ({ form }: DestinationNameInputProps) => {
+export const DestinationNameInput = ({ form, destinationType }: DestinationNameInputProps) => {
return (
(
Catalog
- The selected project's Postgres database is used as the PostgreSQL DuckLake catalog
+ The selected project's Postgres database is used as the DuckLake catalog.
@@ -197,14 +204,11 @@ const DuckLakeSupabaseFields = ({ form }: { form: UseFormReturn (
{renderRegionWarning(field.value)}
-
- Pipelines connects to this project's Postgres instance to store the DuckLake
- catalog
-
+ {DUCKLAKE_CATALOG_PROJECT_FIELD_COPY.description}
}
>
@@ -226,7 +230,7 @@ const DuckLakeSupabaseFields = ({ form }: { form: UseFormReturn
@@ -273,11 +277,11 @@ const DuckLakeSupabaseFields = ({ form }: { form: UseFormReturn (
{renderRegionWarning(field.value)}
- The project whose object storage holds the DuckLake data files
+ {DUCKLAKE_STORAGE_PROJECT_FIELD_COPY.description}
}
>
@@ -302,8 +306,8 @@ const DuckLakeSupabaseFields = ({ form }: { form: UseFormReturn (
@@ -380,7 +384,7 @@ const DuckLakeCustomFields = ({
Catalog
- Configure the PostgreSQL DuckLake catalog and S3-compatible storage for replicated data
+ Configure the Postgres DuckLake catalog and S3-compatible storage for replicated data.
@@ -391,11 +395,11 @@ const DuckLakeCustomFields = ({
render={({ field }) => (
@@ -429,8 +433,8 @@ const DuckLakeCustomFields = ({
render={({ field }) => (
@@ -446,7 +450,7 @@ const DuckLakeCustomFields = ({
@@ -509,7 +513,7 @@ const DuckLakeCustomFields = ({
description={
editMode
? 'Stored secret access key is hidden. Enter a new secret to replace it.'
- : 'Required secret access key for the object storage provider'
+ : 'Required secret access key for the object storage provider.'
}
className="relative"
>
@@ -537,7 +541,7 @@ const DuckLakeCustomFields = ({
@@ -553,7 +557,7 @@ const DuckLakeCustomFields = ({
@@ -569,7 +573,7 @@ const DuckLakeCustomFields = ({
@@ -591,7 +595,7 @@ const DuckLakeCustomFields = ({
diff --git a/apps/studio/components/interfaces/Database/Replication/DestinationPanel/DestinationForm/Snowflake/Fields.tsx b/apps/studio/components/interfaces/Database/Replication/DestinationPanel/DestinationForm/Snowflake/Fields.tsx
index 8a7cff2a386..137f9fde687 100644
--- a/apps/studio/components/interfaces/Database/Replication/DestinationPanel/DestinationForm/Snowflake/Fields.tsx
+++ b/apps/studio/components/interfaces/Database/Replication/DestinationPanel/DestinationForm/Snowflake/Fields.tsx
@@ -7,6 +7,11 @@ import { FormItemLayout } from 'ui-patterns/form/FormItemLayout/FormItemLayout'
import { STORED_SECRET_PLACEHOLDER } from '../DestinationForm.constants'
import type { DestinationPanelSchemaType } from '../DestinationForm.schema'
+import {
+ SNOWFLAKE_ACCOUNT_ID_FIELD_COPY,
+ SNOWFLAKE_DATABASE_FIELD_COPY,
+ SNOWFLAKE_SCHEMA_FIELD_COPY,
+} from '../DestinationFormFieldCopy'
import { MAX_PRIVATE_KEY_LENGTH, readPrivateKeyFile } from './Snowflake.utils'
export const SnowflakeFields = ({
@@ -70,8 +75,8 @@ export const SnowflakeFields = ({
render={({ field }) => (
@@ -111,8 +116,8 @@ export const SnowflakeFields = ({
render={({ field }) => (
@@ -127,8 +132,8 @@ export const SnowflakeFields = ({
render={({ field }) => (
diff --git a/apps/studio/components/interfaces/Database/Replication/DestinationPanel/DestinationForm/index.tsx b/apps/studio/components/interfaces/Database/Replication/DestinationPanel/DestinationForm/index.tsx
index 29ac41fd49f..59a22fac1d3 100644
--- a/apps/studio/components/interfaces/Database/Replication/DestinationPanel/DestinationForm/index.tsx
+++ b/apps/studio/components/interfaces/Database/Replication/DestinationPanel/DestinationForm/index.tsx
@@ -287,10 +287,10 @@ export const DestinationForm = ({
return existingDestination?.enabled ? 'Apply and restart pipeline' : 'Apply changes'
} else {
if (hasRunValidation && validationWarnings.length > 0 && !hasValidationFailures) {
- return 'Create and start pipeline anyway'
+ return 'Start pipeline anyway'
}
- return 'Create and start pipeline'
+ return 'Start pipeline'
}
}
@@ -474,7 +474,7 @@ export const DestinationForm = ({
Destination details
-
+
setPublicationPanelVisible(true)}
diff --git a/apps/studio/components/interfaces/Database/Replication/DestinationPanel/DestinationPanel.tsx b/apps/studio/components/interfaces/Database/Replication/DestinationPanel/DestinationPanel.tsx
index 17821243c46..15ca208b799 100644
--- a/apps/studio/components/interfaces/Database/Replication/DestinationPanel/DestinationPanel.tsx
+++ b/apps/studio/components/interfaces/Database/Replication/DestinationPanel/DestinationPanel.tsx
@@ -31,6 +31,14 @@ import { checkLocalETLNotSetUp } from '@/data/replication/utils'
import { useConfirmOnClose } from '@/hooks/ui/useConfirmOnClose'
import { DOCS_URL } from '@/lib/constants'
+const DESTINATION_DOCS_PATHS: Partial> = {
+ BigQuery: '/guides/database/replication/pipelines/bigquery#configure-bigquery-as-a-destination',
+ ClickHouse:
+ '/guides/database/replication/pipelines/clickhouse#configure-clickhouse-as-a-destination',
+ DuckLake: '/guides/database/replication/pipelines/ducklake#choose-a-configuration-mode',
+ Snowflake: '/guides/database/replication/pipelines/snowflake#prepare-snowflake-resources',
+}
+
export const DestinationPanel = () => {
const { ref: projectRef } = useParams()
const enablePgReplicate = useIsETLPrivateAlpha()
@@ -97,10 +105,10 @@ export const DestinationPanel = () => {
onClose,
})
- const docsUrl =
- destinationType === 'BigQuery'
- ? `${DOCS_URL}/guides/database/replication/bigquery#configure-bigquery-as-a-destination`
- : `${DOCS_URL}/guides/database/replication/pipelines#step-3-configure-a-destination`
+ const docsUrl = `${DOCS_URL}${
+ (destinationType && DESTINATION_DOCS_PATHS[destinationType]) ??
+ '/guides/database/replication/pipelines#step-3-configure-a-destination'
+ }`
useEffect(() => {
if (edit !== null && invalidExistingDestination) {
diff --git a/apps/studio/components/interfaces/Database/Replication/DestinationPanel/DestinationTypeSelection.tsx b/apps/studio/components/interfaces/Database/Replication/DestinationPanel/DestinationTypeSelection.tsx
index 4747a703566..f7c2eeb0279 100644
--- a/apps/studio/components/interfaces/Database/Replication/DestinationPanel/DestinationTypeSelection.tsx
+++ b/apps/studio/components/interfaces/Database/Replication/DestinationPanel/DestinationTypeSelection.tsx
@@ -74,7 +74,7 @@ export const DestinationTypeSelection = () => {
{
value: 'BigQuery',
label: 'BigQuery',
- description: "Replicate changes to Google Cloud's data warehouse for analytics and BI",
+ description: 'Replicate changes to BigQuery for analytics and BI',
stage: 'Public Alpha',
enabled: isOptionVisible('BigQuery', etlEnableBigQuery),
},
@@ -86,22 +86,21 @@ export const DestinationTypeSelection = () => {
{
value: 'DuckLake',
label: 'DuckLake',
- description: 'Replicate changes to a DuckLake catalog backed by S3-compatible storage',
+ description: 'Replicate changes to DuckLake for open lakehouse storage',
stage: 'Early Access',
enabled: isOptionVisible('DuckLake', etlEnableDucklake),
},
{
value: 'Snowflake',
label: 'Snowflake',
- description:
- 'Replicate changes to Snowflake for warehouse analytics and downstream data workflows',
+ description: 'Replicate changes to Snowflake for cloud data warehousing',
stage: 'Early Access',
enabled: isOptionVisible('Snowflake', etlEnableSnowflake),
},
{
value: 'ClickHouse',
label: 'ClickHouse',
- description: 'Stream changes to a ClickHouse cluster for fast columnar analytics',
+ description: 'Replicate changes to ClickHouse for real-time analytics',
stage: 'Early Access',
enabled: isOptionVisible('ClickHouse', etlEnableClickHouse),
},
@@ -113,7 +112,7 @@ export const DestinationTypeSelection = () => {
{
value: 'Analytics Bucket',
label: 'Analytics Bucket',
- description: 'Write Apache Iceberg tables to Supabase Storage for analytics workflows',
+ description: 'Replicate changes to Supabase Storage as Apache Iceberg tables',
stage: 'Deprecated',
enabled: isOptionVisible('Analytics Bucket', etlEnableIceberg),
},