Slack Canvas vs Coda: Architecture, Limits and Migration
Coda is a document-database hybrid with formulas, buttons, and Packs. Slack Canvas is a prose surface with a 300-cell table cap and zero computation. Here's what a migration actually requires.
Planning a migration?
Get a free 30-min call with our engineers. We'll review your setup and map out a custom migration plan — no obligation.
Schedule a free call- 1,500+ migrations completed
- Zero downtime guaranteed
- Transparent, fixed pricing
- Project success responsibility
- Post-migration support included
Slack Canvas vs Coda: Architecture, Limits and Migration
Slack Canvas is a lightweight prose surface embedded in Slack. Coda is a document-database hybrid whose tables carry formulas, whose buttons trigger actions, whose automations run on schedules, and whose Packs pull data from external systems. The gap between them is the widest in the productivity-tool landscape — even wider than Canvas vs Notion — a Coda doc of any real complexity has no Canvas representation, only a prose summary of one.
This guide covers what each tool does, where Canvas hits its ceilings, when it still earns its place, and what a Coda-to-Slack move actually requires. It is written for engineers and team leads evaluating or executing this migration, not for teams that have already decided.
Important: Not all migrations require outside help. If your Coda workspace consists primarily of prose pages with small static tables, a Canvas migration is a scripting exercise of moderate complexity. The sections below identify exactly where complexity appears so you can make that judgment yourself.
Rebranding note: Coda was acquired by Grammarly in 2024. The product at coda.io continues to operate under the Coda name. This article uses "Coda" throughout because that remains the primary search term and the active product name at the time of writing.
Last verified: Canvas API limits and Coda API limits change. The specific values in this document were verified against Slack API documentation and Coda API documentation in mid-2025. Check the linked references before relying on these numbers in production scripts.
What Is Slack Canvas?
Slack Canvas is a document surface built into Slack where members can create and share formatted content that outlives a message thread. Canvases can exist attached to a channel or as standalone documents on paid plans.
Canvas supports a defined set of formatting elements: blockquote, bold, bulleted lists, callout, checklist, code block, code span, column layout, divider, emojis, headings (H1–H3), italic, links, markdown tables, ordered lists, paragraph, quote block, and strikethrough.
What Canvas does not have (despite its Quip-based architecture):
- No spreadsheet or formula engine. Tables are static grids.
- No buttons, automations, or interactive app surfaces.
- No Block Kit support. Block Kit is explicitly unsupported in canvases.
- No computed columns, conditional formatting, or data types.
Canvas is a reading-and-editing surface. It is not a database, not a workflow engine, and not an app platform.
What Is Coda?
Coda is a hosted, multi-user document editor that combines text, structured tables, formulas, buttons, and third-party integrations (Packs) into a single building-block system. It was designed around the idea that documents and applications should be the same thing.
The architecture matters for any migration conversation:
- Tables are the single source of truth. Cells can contain complex formulas, cross-table lookups, and buttons that trigger external API calls.
- Buttons and Automations change state and post updates. Automations run on schedules, webhooks, or row-change triggers.
- Packs connect external systems — Slack, Jira, GitHub, Google Calendar, HubSpot, Salesforce, and hundreds of others via the Coda Pack directory.
- Synced tables pull live data from external sources (Jira issues, GitHub PRs, Google Calendar events) and behave differently from native tables during API export — a critical migration distinction covered below.
- Published docs can be shared via public URL without requiring Slack access — a capability Canvas has no equivalent for.
- A Coda doc is a single container with a tree of sub-pages, its own sharing settings, its own published URL, and its own runtime environment.
A Coda doc with three linked tables, a dozen formula columns, four buttons, and a Jira Pack sync is not a document — it is an application. That distinction drives everything below.
How Do Slack Canvas and Coda Compare Architecturally?
The difference is architectural, not cosmetic. Slack deliberately separates long-form documents (Canvas), structured records (Lists), and custom interactivity (messages, modals, App Home via Block Kit) across different surfaces. Coda puts all of those ideas into one model: tables relate to other tables, formulas live on the canvas and in tables, buttons sit on pages or in rows, automations chain actions, and Packs add sync tables and external-system behavior.
| Capability | Slack Canvas | Coda |
|---|---|---|
| Core model | Prose document surface | Document-database hybrid with runtime |
| Tables | Static markdown grid, 300-cell cap | Relational tables with formulas, lookups, views |
| Formulas | None | Full formula language across tables and canvas |
| Buttons / interactivity | None | Buttons that write rows, call Packs, send messages |
| Automations | None (Workflow Builder is a separate surface) | Native rules: schedule, webhook, row-change triggers |
| Third-party integrations | None inside the canvas | Packs directory with 100+ official integrations |
| Synced tables | Not applicable | Live-sync from Jira, GitHub, Calendar, etc. |
| Published public URL | No (requires Slack login) | Yes, docs can be published publicly |
| Image/file handling | Inline images, no file attachments | Inline images, file attachment columns in tables |
| Permission granularity | Canvas-level only | Doc, page, and table-level permissions |
| Headings | H1, H2, H3 only | Full heading hierarchy |
| Document size (API) | 1 MiB per document_content object |
125 MB doc-size limit for API access |
| Block Kit | Not supported | N/A (different paradigm) |
| Standalone access | Paid plans only | Free tier with limits |
Canvas covers the content column and nothing else. Every row in the Coda column that says "buttons," "formulas," "Packs," or "automations" has no Canvas equivalent — not a weaker equivalent, but zero.
Slack's own architecture acknowledges the gap. Slack Lists — a separate surface — handle structured records with typed fields, filtering, sorting, table and board layouts, item threads, and list-specific workflows. Lists are closer to a Coda table than Canvas is, though they still lack Coda's formula engine, Packs, and cross-table relations.
What Are Slack Canvas's Hard Limits?
Before evaluating Canvas as a migration target, know the ceilings you will hit.
Tables: 300 cells, no formulas, no computation
Canvas tables have a limit of 300 cells per table — any combination of rows and columns that totals 300. A 10-column table maxes out at 30 rows. A 5-column table gets 60. There is no way to extend this.
Canvas tables accept basic inline formatting (bold, italic, links) but have no cell types, no number formatting, no sorting, no filtering, and no formulas. If you need =SUM(B2:B30), Canvas cannot help you.
By contrast, Coda tables can handle tens of thousands of rows, complex schema definitions, and dynamic filtering. Any Coda table exceeding 300 cells has no native representation in Canvas.
The 300-cell cap is per table, not per canvas. You can have multiple tables in one canvas, each independently capped at 300 cells. None of them compute anything.
Headings: H1–H3 only
The Canvas API accepts headings H1 through H3. If your Coda doc uses deeper heading nesting (H4+), those levels must be flattened to H3 or converted to bold text during transformation. There is no automatic downgrade — your migration script must handle this explicitly.
Document size: 1 MiB per document_content object
The markdown content is limited to 1 MiB (1,048,576 characters) per document_content object. When editing a canvas, this limit applies to each change in the changes array. That is roughly 200,000 words of plain text — generous for prose, but a real constraint for programmatic content generation or documents with large embedded images.
One channel canvas per channel
Calling conversations.canvases.create when a channel canvas already exists returns a channel_canvas_already_exists error. On paid plans, you can create standalone canvases and add them as tabs to channels. On the free tier, you are limited to one canvas per channel or DM.
Standalone canvases: paid plans only
Channel and DM canvases are available on all Slack plans. Standalone canvases require a paid plan. If your workspace is on the free tier, your automation must target a conversation — channel_id is required.
No interactive surface
Canvas has no mechanism for a button that writes a database row, no way to trigger an automation from within the document, and no concept of a Pack or integration that lives inside the canvas. Slack's Workflow Builder exists separately but cannot embed interactive elements inside canvas content.
Image and file handling
Canvas supports inline images rendered from URLs. It does not support file attachments. Coda table columns of type "file" or "image" — where the file is stored in Coda's own storage — cannot be directly embedded in Canvas. You must re-host those files externally (S3, Google Drive, etc.) and reference them by URL. Coda's API does not provide a bulk file export endpoint; file attachments in table cells must be downloaded individually via the row data URLs before re-hosting.
Access control mismatch
Coda supports permissions at three levels: doc, page, and table. Canvas supports access at the canvas level only, set via canvases.access.set. If your Coda workspace uses page-level or table-level access restrictions — for example, a financial table visible only to the finance team within a broader team doc — those granular permissions have no Canvas equivalent. You must restructure content boundaries before migration, moving restricted content into separate canvases with appropriate access controls.
API throughput
| Operation | Slack API | Coda API |
|---|---|---|
canvases.create |
Tier 2: 20+ req/min | — |
canvases.edit |
Tier 3: 50+ req/min | — |
| Table row reads | — | 100 req/6 sec |
| Table row writes | — | 10 req/6 sec |
| Rows per page (List Rows) | — | 500 rows max |
| Max row payload | — | 85 KB per row |
| Max API request body | — | 2 MB |
Plan your migration scripts around these ceilings. A workspace with 10,000 Coda rows across multiple tables requires careful pagination and rate-limit handling on the read side before you make a single Canvas write call.
When Does Slack Canvas Make Sense?
Canvas earns its place when a document's primary job is to be read and lightly edited inside a channel, and proximity to the conversation is worth more than structural complexity.
Good fits:
- Channel onboarding docs — team norms, links to key resources, "read this before posting" guides.
- Meeting notes pinned to a project channel — attendees edit in-place, no context-switching to another app.
- Incident runbooks — a checklist that lives next to the incident channel, updated in real time during an outage.
- Lightweight status pages — a small table (under 300 cells) summarizing project status for a channel audience.
- Project briefs and weekly summaries — content that explains or orients rather than computes.
The common thread: the reader is already in Slack, the content is mostly prose or small static tables, and the value of not leaving Slack outweighs the value of formulas, automations, or structured data.
Canvas is the wrong target if the document:
- Relies on formula columns for computed values
- Uses cross-table lookups or relational data
- Contains buttons that trigger workflows
- Syncs data from external systems via Packs or synced tables
- Exceeds 300 cells in any single table
- Requires heading depth beyond H3
- Has page-level or table-level access restrictions
- Is shared externally via a published public URL
- Contains file attachments stored in Coda's native file storage
Use Canvas when the content is the destination. If the document mainly explains, summarizes, or orients, Canvas fits. If the document calculates, routes, syncs, or acts on data, you need a structured backend somewhere else.
What Does a Coda-to-Slack-Canvas Migration Require?
Moving from Coda to Slack Canvas is not a migration in the traditional sense — it is a decomposition. You are splitting one integrated system into at least two destinations: Canvas for prose, and something else for the structured data that Canvas cannot hold.
Step 1: Audit your Coda docs
Classify every doc into one of three buckets:
- Prose-only docs — text, images, simple checklists with no formula columns, no buttons, and tables under 300 cells. These can move to Canvas with a scripted transformation.
- Hybrid docs — prose plus tables with formulas, Packs, or relational columns. The prose moves to Canvas; the tables need a second destination.
- App-like docs — the doc is the workflow (buttons, automations, Pack syncs, synced tables, cross-doc references). Canvas cannot represent these. The entire doc needs a different destination.
In practice, bucket-1 docs are often the minority in mature Coda workspaces because Coda's design actively encourages adding formula columns and buttons. However, this is an empirical claim that you must verify for your specific workspace — audit before assuming complexity.
Quick audit checklist per doc:
- Does any table have a formula column? → Not bucket 1
- Does any table have a relation/lookup column? → Not bucket 1
- Does the doc contain any buttons? → Not bucket 1
- Does the doc use any Packs or synced tables? → Not bucket 1
- Does any table exceed 300 cells? → Not bucket 1
- Is the doc published to a public URL? → Canvas cannot replicate public access
- Does the doc have page- or table-level access controls? → Restructuring required
- Does any table contain file or image column attachments? → Re-hosting required
A doc that passes all checks is a genuine candidate for direct Canvas migration. A doc that fails any check requires decomposition planning.
Step 2: Choose a second destination for structured data
The tables, formulas, and automations need somewhere to live. Common choices:
| Destination | Good for | Limitations |
|---|---|---|
| Google Sheets | Formula-heavy tables, familiar UI | No buttons, no Packs, automation requires Apps Script |
| Notion | Teams wanting docs + databases in one place | Weaker formula engine; no native buttons |
| Airtable | Relational data, automations, integrations | Separate from your doc layer; row-count limits on lower tiers |
| Slack Lists | Simple task/status trackers already in Slack | No formulas, no Packs, limited field types |
| Custom internal tooling | Teams with engineering capacity | Maintenance burden; not a drop-in replacement |
No single tool replicates Coda's "doc + database + automation + Packs" model in one surface. That is the real cost of this migration — you are unbundling something that was designed to be bundled.
Step 3: Extract content from Coda
The Coda API is a RESTful API that lets you programmatically interact with Coda docs — reading data from tables, accessing computed formula values, and exporting page content.
Key extraction constraints:
- Doc size limit: Docs larger than 125 MB cannot access the Coda API. Split these manually before extraction.
- Row payload: The API request limit is 2 MB, with a limit of 85 KB for any given row.
- Pagination: The List Rows endpoint (
GET /docs/{docId}/tables/{tableId}/rows) returns up to 500 rows per page. Implement pagination. - Rate limits: Read rate is 100 requests per 6 seconds; write rate is 10 requests per 6 seconds.
Reference column resolution
When a Coda table has a relation column pointing to another table, the API returns the row ID of the referenced item — not the plaintext value. Your extraction script must pull both tables, build an ID-to-value map in memory, and flatten the data before writing to your secondary destination.
// Coda API row response: reference column returns row ID, not plaintext
{
"id": "i-123456789",
"type": "row",
"name": "Feature Request 42",
"values": {
"c-987654321": "Build new API endpoint",
"c-123123123": "i-999999999" // Row ID reference — must be resolved manually
}
}Synced table behavior
Synced tables (tables whose data originates from a Jira, GitHub, or other Pack integration) behave differently from native Coda tables during API export:
- The rows endpoint returns the last-synced snapshot of external data, not a live query.
- Column IDs for synced table columns are generated differently and may not match the schema of native tables.
- The sync metadata (sync interval, last-sync timestamp, source configuration) is not exposed via the rows API and is not recoverable programmatically.
If you are migrating a synced table, you are extracting a snapshot, not replicating the live integration. The integration itself must be rebuilt in your destination system natively (for example, a Jira integration configured directly in Airtable or Google Sheets).
Image and file extraction
Coda's API returns URLs for file and image column values. These URLs are authenticated and time-limited — they are not stable public URLs. To migrate file attachments:
- Extract row data including file column values.
- Download each file using the authenticated URL before it expires.
- Re-upload to your external storage (S3, Google Drive, etc.).
- Replace the Coda URL with your stable external URL in the destination schema.
There is no bulk file export endpoint. File migration is row-by-row.
Published docs
If a Coda doc is published at a public URL (e.g., coda.io/d/_dXXXXXX), Canvas has no equivalent. Canvas requires a Slack login to view. If your organization shares docs externally with non-Slack users, Canvas cannot replace this use case. Consider keeping the published doc on Coda or migrating to a public-facing tool (Notion public pages, Confluence, a static site).
Step 4: Transform and write to Slack Canvas — worked example
Use canvases.create for standalone canvases or conversations.canvases.create for channel canvases. Both accept a document_content object with "type": "markdown".
Required transformations:
- Downgrade headings: Convert all H4, H5, H6 to H3 or bold text.
- Handle tables: If under 300 cells, include as a markdown table. If over 300 cells, create a brief summary table in Canvas with a link to the authoritative data in your second destination.
- Strip interactive elements: Remove all button references, Pack integrations, and formula outputs. Replace with static placeholder text or links.
- Monitor payload size: If the transformed document approaches 1 MiB, split into multiple canvases.
- Set permissions: Use
canvases.access.setto configure access.
Complete worked example: hybrid Coda doc to Canvas + Google Sheets
Suppose you have a Coda doc with:
- One prose section (team norms, ~800 words)
- One native table: "Projects" with 8 columns × 20 rows (160 cells, under limit), including a formula column computing
Days Remaining = DueDate - Today() - One relation column in Projects linking to an "Owners" table (separate table)
Migration sequence:
import requests, json
CODA_API_KEY = "your-coda-token"
SLACK_TOKEN = "your-slack-token"
DOC_ID = "your-doc-id"
TABLE_ID = "your-table-id"
OWNERS_TABLE_ID = "your-owners-table-id"
CHANNEL_ID = "C01234567"
# Step 1: Pull Owners table to build ID→name map
owners_resp = requests.get(
f"https://coda.io/apis/v1/docs/{DOC_ID}/tables/{OWNERS_TABLE_ID}/rows",
headers={"Authorization": f"Bearer {CODA_API_KEY}"},
params={"limit": 500, "valueFormat": "simple"}
)
owners = {row["id"]: row["values"]["c-name-col-id"]
for row in owners_resp.json()["items"]}
# Step 2: Pull Projects table with pagination
all_rows = []
page_token = None
while True:
params = {"limit": 500, "valueFormat": "simple"}
if page_token:
params["pageToken"] = page_token
resp = requests.get(
f"https://coda.io/apis/v1/docs/{DOC_ID}/tables/{TABLE_ID}/rows",
headers={"Authorization": f"Bearer {CODA_API_KEY}"},
params=params
)
data = resp.json()
all_rows.extend(data["items"])
page_token = data.get("nextPageToken")
if not page_token:
break
# Step 3: Resolve relation columns and flatten
for row in all_rows:
owner_ref = row["values"].get("c-owner-col-id")
row["values"]["owner_name"] = owners.get(owner_ref, "Unknown")
# Formula column (Days Remaining) — freeze as static value from API response
# The API returns the computed value at extraction time; it will not update in Canvas
# Step 4: Build markdown table for Canvas (160 cells, under 300-cell limit)
# Note: Days Remaining column is now static — annotate this in Canvas
header = "| Project | Owner | Due Date | Days Remaining (snapshot) |\n"
divider = "|---|---|---|---|\n"
rows_md = ""
for row in all_rows:
v = row["values"]
rows_md += f"| {v.get('c-project-col-id','')} | {v.get('owner_name','')} | {v.get('c-due-col-id','')} | {v.get('c-days-col-id','')} |\n"
# Step 5: Build canvas markdown
prose_content = "## Team Norms\n\n[...prose content extracted from Coda page...]\n\n"
table_note = "> ⚠️ Days Remaining values are a static snapshot from migration date. See Google Sheets for live data.\n\n"
canvas_md = prose_content + "## Projects\n\n" + table_note + header + divider + rows_md
# Step 6: Check payload size
if len(canvas_md.encode("utf-8")) > 1_048_576:
raise ValueError("Canvas payload exceeds 1 MiB — split into multiple canvases")
# Step 7: Write to Slack Canvas
canvas_resp = requests.post(
"https://slack.com/api/conversations.canvases.create",
headers={
"Authorization": f"Bearer {SLACK_TOKEN}",
"Content-Type": "application/json"
},
json={
"channel_id": CHANNEL_ID,
"document_content": {
"type": "markdown",
"markdown": canvas_md
}
}
)
result = canvas_resp.json()
if not result.get("ok"):
print(f"Canvas creation failed: {result.get('error')}")
# See failure modes section below
else:
print(f"Canvas created: {result['canvas_id']}")What this example demonstrates:
- Pagination on the Coda rows endpoint
- Reference column resolution (Owners lookup)
- Freezing a formula column as a static snapshot with an explicit annotation in the Canvas
- Payload size check before the API call
- Error handling on the Canvas write
The Projects table (160 cells) fits in Canvas. The Days Remaining formula column is preserved as a static value with a clear note that it no longer updates. The Owners relation is flattened to plaintext. The live formula behavior must be rebuilt in Google Sheets if it is needed.
Step 5: Rebuild automation and compute
Because Canvas has no compute capabilities, any workflow previously handled by Coda automations, buttons, or Packs must be rebuilt externally:
Coda page prose → Slack Canvas (direct migration)
Coda native table → Slack List or external system of record
Coda synced table → Native integration in destination system (snapshot only from API)
Coda relation column → Foreign key equivalent in target system (resolve at extraction)
Coda formula column → Rebuild in destination system, or freeze as static snapshot
Coda button → Workflow action, custom Slack app, or retirement candidate
Coda Pack integration → Native integration in destination, ETL sync, or retirement
Coda automation → Workflow Builder (Slack), Zapier, Make, or destination-native
Coda published URL → No Canvas equivalent — keep on Coda or migrate to public tool
Coda page-level ACL → Separate Canvas per permission boundary
Coda file attachment → Re-hosted external URL (manual download required)If a Coda button previously approved an expense and notified a channel, that logic must move to a Slack Workflow, a custom Slack App using Block Kit (sent as a message, not stored in Canvas), or a third-party tool like Zapier or Make. Slack's Workflow Builder does not currently include a native Coda connector. A third-party automation tool or custom integration is required for no-code bridging.
Failure Modes and Recovery
Migration scripts fail. Know the failure modes before you start.
Canvas creation failures:
| Error | Cause | Recovery |
|---|---|---|
channel_canvas_already_exists |
A canvas already exists for this channel | Use canvases.edit on the existing canvas ID, or create a standalone canvas |
invalid_arguments |
Malformed document_content object |
Validate markdown structure; check for unescaped special characters |
payload_too_large |
Content exceeds 1 MiB | Split content into multiple canvases; use links between them |
not_in_channel |
Bot is not a member of the target channel | Invite the bot to the channel before calling the API |
plan_upgrade_required |
Standalone canvas on free plan | Use conversations.canvases.create with a channel_id, or upgrade |
ratelimited |
Exceeded Tier 2/3 limits | Implement exponential backoff; respect Retry-After header |
Coda extraction failures:
| Error | Cause | Recovery |
|---|---|---|
| Doc exceeds 125 MB | Doc too large for API access | Split doc manually in Coda UI before extraction |
| 401 Unauthorized | Expired or invalid API token | Regenerate token in Coda account settings |
| 429 Too Many Requests | Exceeded 100 reads/6 sec | Implement rate limiting with 60 ms delay between reads |
| Row payload > 85 KB | Single row has very large content | Identify the large column (likely file attachment URL blob); handle separately |
| Reference ID not in map | Related row was deleted or in another doc | Log unresolved references; set value to null with a migration note |
Data loss risks:
- Formula columns that reference the current date (
Today(),Now()) will be frozen at extraction time. The extracted value will become stale immediately. - Synced table rows deleted in the source system before extraction will not appear in the snapshot.
- File attachments not downloaded before the authenticated URL expires are unrecoverable from the API.
- Cross-doc references (tables or pages in other Coda docs) are not traversed by a single-doc extraction. You must audit and extract referenced docs separately.
Rollback:
Slack Canvas provides no version history via API. If you overwrite a channel canvas with bad content, you cannot roll back via API — you must re-run the migration script with corrected content. Before writing to a production channel canvas, test against a staging channel. Keep your extracted Coda data as the source of truth until the Canvas content is verified by a human reviewer.
What You Lose in This Migration
Be explicit with stakeholders:
- All formula columns. Computed values become static snapshots at extraction time. They will never update again unless rebuilt in the destination system.
- All buttons and automations. Must be rebuilt in another system — Zapier, Make, native automation in your second destination, or custom code.
- All Pack integrations and synced tables. The Jira sync, the Slack notifications triggered from Coda, the Salesforce pull — all are snapshots at extraction time. Rebuild them as standalone integrations.
- Cross-doc references. These break entirely. There is no Canvas equivalent and no automated resolution path.
- Relational table structure. Lookups between tables, filtered views, grouped views — none exist in Canvas.
- Public URL sharing. Externally shared docs have no Canvas equivalent. This use case requires a different tool.
- Granular access controls. Page-level and table-level permissions must be restructured into separate canvases.
- File attachments. Must be re-hosted; cannot be embedded directly from Coda storage.
This is not a criticism of Slack Canvas. Canvas was designed to give Slack channels a persistent document surface — and it does that well. It was never designed to replace Coda.
Should You Migrate or Integrate?
Before committing to a full migration, consider whether integration is the better answer. Coda's own Slack Pack can pull messages into a doc and send Slack updates or reminders from the doc. In many environments, keeping Coda as the structured-data backend while using Canvas for prose summaries and channel context is a more honest architecture than forcing everything into Slack.
The question is not "Slack Canvas vs Coda?" — it is "What was my Coda doc actually doing?"
- If it was mostly prose → Canvas can replace it directly.
- If it was computational → Canvas is at best a static summary layer that links to where the real work happens.
- If the doc's value was the integration between prose and data in one surface → unbundling into Canvas + a second system is a real architectural trade-off, not just a tooling switch.
Answering that question honestly, per doc, is the audit in Step 1. Do the audit before writing a single line of migration code.
Further Reading
- Coda vs Notion comparison — how Coda's data architecture maps to Notion's database model
- Coda export guide — extracting docs, tables, and workspace data
- Workflow preservation checklist — preserving automation logic across platform migrations
Frequently Asked Questions
- Can Slack Canvas replace Coda?
- No. Slack Canvas is a prose editing surface with no formulas, no buttons, no automations, and no Pack integrations. It can hold text and small static tables (up to 300 cells per table), but any Coda doc that uses computed columns, cross-table lookups, or workflow automation has no Canvas equivalent.
- What is the Slack Canvas table limit?
- Slack Canvas tables are capped at 300 cells per table, in any combination of rows and columns (e.g., 10 columns × 30 rows). Tables have no formulas, no sorting, no filtering, and no cell data types — they are static grids with basic text formatting.
- Are Slack Canvases available on the free plan?
- Channel and DM canvases are available on all Slack plans, but standalone canvases require a paid plan. Free workspaces are limited to one canvas per channel or DM.
- How do you migrate data from Coda to Slack Canvas?
- You extract prose content from Coda via its API as Markdown, then POST it to the Slack Canvas API as a document_content object. Tables with formulas, buttons, and automations cannot move to Canvas — they need a second destination like Google Sheets, Notion, Airtable, or Slack Lists.
- What is the maximum size of a Slack Canvas?
- Each document_content object in the Slack Canvas API is limited to 1 MiB (1,048,576 characters) of markdown content. This limit applies per API call when creating or editing a canvas.