softr-vibe-coding 1.11.2 → 2.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +10 -0
- package/README.md +32 -22
- package/SKILL.md +40 -26
- package/datasources/airtable.md +1 -0
- package/datasources/fields.md +4 -4
- package/datasources/monday.md +8 -1
- package/datasources/multi-datasource.md +108 -0
- package/datasources/notion.md +2 -2
- package/datasources/overview.md +1 -1
- package/datasources/reading.md +29 -7
- package/datasources/rest-api.md +20 -1
- package/datasources/shared-patterns.md +2 -1
- package/datasources/softr-database.md +6 -6
- package/datasources/writing.md +166 -60
- package/package.json +1 -1
- package/references/airtable-automations.md +1 -1
- package/references/anti-patterns.md +18 -8
- package/references/common-patterns.md +1 -1
- package/references/helper-blocks.md +38 -6
- package/references/native-block-filters.md +1 -1
- package/references/quick-reference.md +57 -14
- package/references/softr-mcp.md +140 -0
- package/references/softr-database-mcp.md +0 -59
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,16 @@ All notable changes to this skill are documented here. Versions follow [Semantic
|
|
|
4
4
|
|
|
5
5
|
Entries from 1.3.1 onward are generated automatically from git commit subjects between version bumps (see `.github/workflows/publish.yml`). Entries before 1.3.1 were backfilled by hand from the existing commit history.
|
|
6
6
|
|
|
7
|
+
## [2.1.0] - 2026-08-25
|
|
8
|
+
- Bump to 2.1.0 — official dev-guide sync (field-name rule for Airtable/Notion/Sheets, PHONE sanitization, useProxyFetch datasource routing, useCurrentUser properties) + workspace-wide Softr MCP reference with direct block deployment (softr-mcp.md replaces softr-database-mcp.md)
|
|
9
|
+
- Sync with the official Vibe Coding developer guide and the workspace-wide Softr MCP server (28 verified fixes). Field-name rule extended: Airtable AND Notion AND Google Sheets use field NAMES in q.select (Softr DB/Supabase use IDs; wrong form fails silently with empty data) — fixed in Hard Constraint 11, notion.md, helper-blocks.md publisher template (was modeling fldXXX IDs), fields.md. New PHONE write rule: + followed by digits only, Monday.com hard-rejects formatted values — official sanitizePhone added to writing.md (new Phone section), monday.md, anti-patterns.md. useProxyFetch is datasource-scoped: alias as its ARGUMENT (useProxyFetch(ds.store)), throws when omitted with >1 source; proxy payloads are text-only (no FormData/streams/uploads) — added to rest-api.md, multi-datasource.md, quick-reference.md, anti-patterns.md. fetchNextPage rule relaxed to match the guide's canonical Load More onClick: never in the render body, event handler or guarded useEffect both fine (SKILL.md x2, README, anti-patterns.md). useCurrentUser({ properties }) exposes custom user fields under user.properties — window.__softr_current_user narrowed to userGroups/role only; id only present with user sync (reading.md, quick-reference.md, fields.md). useLinkedRecords count default 100 max 1000. useUpload: isUploading + result.error handling + multi-file pattern. Linked-record field-type row corrected to string-array shape. TRIGGER_CUSTOM_WORKFLOW takes no destination per the documented type. references/softr-mcp.md REPLACES softr-database-mcp.md: the MCP is now workspace-wide — vibe coding block tools (get_vibe_coding_docs, create/edit/versions, data source wiring + the five official gotchas incl. action-permission reset on every code change), integrations browsing down to field level (Airtable/Sheets/Notion/Supabase/Softr DB only), databases tools (no deletes yet; 100-create/200-read/2-group-by limits), three-area bundled permission levels replacing the old granular scopes. SKILL.md workflow now offers direct MCP deployment alongside paste-into-Studio. Description gains negative scope (building-design-md, non-Softr dashboards); reading/writing/fields now linked directly from SKILL.md instead of only via the shared-patterns index; README tree gains the tools/ dir; retired "no ?." mention dropped from airtable-automations.md; common-patterns var-style sentence reworded to legacy-but-valid
|
|
10
|
+
- Bump to 2.0.0 — verified 2026-08 platform contract: modern TypeScript, mutateAsync write queues, label-string SELECT writes, flat create payloads, inline hook options, Actions reset on recompile
|
|
11
|
+
|
|
12
|
+
## [1.12.0] - 2026-07-22
|
|
13
|
+
- Bump to 1.12.0 — multi-datasource blocks, Airtable rating writability, dark-brand canvas + page-background anti-patterns
|
|
14
|
+
- Document the Airtable rating field as writable (plain integer 0-max; 0 clears it, so AVERAGE formulas skip it) — it was absent from the Supported Fields table, which made it the main unknown when building a star-rating form. Add two dark-brand styling anti-patterns: a block on a dark brand MUST paint its own backgroundColor because custom-code-header's body rule doesn't cross the shadow-DOM boundary and a default Softr page is white, so white text/logos/buttons render invisibly on white (the existing don't-double-paint row assumed a light app and inverts here); and setting html,body alone doesn't change the page background because Softr paints the same fill on FOUR stacked layers — html, body, #page-content, and a class-less wrapper div inside it — so paint html then clear the duplicates, excluding the .softr-topbar subtree
|
|
15
|
+
- Add datasources/multi-datasource.md — a block can now connect to SEVERAL data sources: datasource.define({alias: 'uuid'}) plus a from: parameter on every data hook (useRecords, useRecord, useLinkedRecords, useFieldOptions, useMetric, useChartData, useRecordCreate/Update/Delete; not useUpload/useCurrentRecordId, which are app-level). Omitting from: throws once >1 source is connected; with exactly one you can skip define entirely. The define() values must be INLINE STRING LITERALS — hoisting them into constants fails to compile with 'datasource.define() object values must be string literals', same static-analysis rule as q.select(). Ids are plain UUIDs, not the table id and not the ds_id_N placeholder shape in Softr's docs; obtain them by asking Studio's AI chat to WRITE CODE, never to recite a value — asked three times in prose for the same three connected tables it returned three different confidently-worded sets, once recycling another table's uuid. This supersedes the one-table-per-block limit: relax Hard Constraint #13 to one useRecords per DATASOURCE, reframe helper-blocks.md around genuinely cross-BLOCK jobs (triggering another block, sharing computed state) with the old rationale kept as history, and retarget the useLinkedRecords anti-pattern at a second datasource rather than a helper
|
|
16
|
+
|
|
7
17
|
## [1.11.2] - 2026-07-08
|
|
8
18
|
- Bump to 1.11.2 — publish recordId-less useRecord note and input.textAsync correction
|
|
9
19
|
- Correct input.textAsync in airtable-automations.md — it is not a real method in EITHER Airtable scripting environment; calling it in the Scripting Extension throws TypeError: input.textAsync is not a function. buttonsAsync is the only interactive runtime prompt; free-text values (e.g. an API key) go through an input.config({...}) setting at the top or a hardcoded constant. Also generalize the Automation Scripts bullet to "no interactive prompts".
|
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Softr Vibe Coding Block — Claude Skill
|
|
2
2
|
|
|
3
|
-
> Turn Claude into a Softr Vibe Coding expert — generate production-ready
|
|
3
|
+
> Turn Claude into a Softr Vibe Coding expert — generate production-ready React blocks (TSX/JSX) with polished UI, correct data fetching, and all 14 Softr data sources supported out of the box.
|
|
4
4
|
|
|
5
5
|

|
|
6
6
|

|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
|
|
13
13
|
## TL;DR
|
|
14
14
|
|
|
15
|
-
This Claude skill teaches Claude Code how to generate complete, polished Softr Vibe Coding blocks as `.jsx` files. It includes:
|
|
15
|
+
This Claude skill teaches Claude Code how to generate complete, polished Softr Vibe Coding blocks as `.tsx` / `.jsx` files (the current platform compiles TypeScript with modern syntax — verified live August 2026). It includes:
|
|
16
16
|
|
|
17
17
|
- **Complete Vibe Coding API reference** — `useRecords`, `q.select()`, mutations, uploads, metrics, charts, editable settings, `useProxyFetch` for REST APIs
|
|
18
18
|
- **All 14 Softr data sources** — Airtable, Softr Database, Google Sheets, HubSpot, Notion, Coda, monday.com, SmartSuite, ClickUp, Xano, Supabase, BigQuery, SQL Database, and REST API — each with field mapping, rate limits, and gotchas
|
|
@@ -20,10 +20,10 @@ This Claude skill teaches Claude Code how to generate complete, polished Softr V
|
|
|
20
20
|
- **Advanced integrations** — Shadow DOM CSS isolation for third-party libraries (Leaflet, Mapbox, TinyMCE, Quill, FullCalendar)
|
|
21
21
|
- **Native shell styling** — re-skin Softr's native top bar, **footer**, nav, dropdowns, and **page background** via global Custom Code CSS (stable selectors vs. hashed classes, floating "island" header/footer, the dropdown grid fix, the multi-layer page-background stacking, restyle-vs-replace) — distinct from blocks
|
|
22
22
|
- **UI/UX design guidelines** — 26 sections covering visual hierarchy, color, typography, spacing, motion design, accessibility, responsive patterns, and an AI slop anti-pattern checklist
|
|
23
|
-
- **Self-validation** — Claude checks for Softr
|
|
23
|
+
- **Self-validation** — Claude checks for Softr platform compatibility (inline hook options, correct payload shapes, correct imports, container wrappers, `getFieldValue()` wrapping, hooks ordering) before delivering code
|
|
24
24
|
- **Premium visual baseline** — Every block ships polished from v1: gradient backgrounds, card elevation, loading skeletons, empty states, error states
|
|
25
25
|
- **Debug utilities** — Field Inspector, API Response Inspector, and User Inspector blocks for diagnosing data source and permissions issues
|
|
26
|
-
- **Softr
|
|
26
|
+
- **Softr MCP integration** — when the [official Softr MCP server](https://docs.softr.io/mcp/overview) is installed (`claude mcp add --transport http softr https://mcp.softr.io/mcp`), Claude reads Softr DB schema, field IDs, and dropdown option UUIDs directly, browses connected Airtable / Google Sheets / Notion / Supabase integrations down to field level, and can even create and deploy Vibe Coding blocks straight into your app — no more pasting `tablespace-with-tables` JSON or copy-pasting code into Studio. See `references/softr-mcp.md`.
|
|
27
27
|
|
|
28
28
|
---
|
|
29
29
|
|
|
@@ -91,7 +91,7 @@ Build me a Softr Vibe Coding block that shows a team directory with cards
|
|
|
91
91
|
This skill is **Step 2** of a two-skill brand-to-blocks pipeline:
|
|
92
92
|
|
|
93
93
|
```
|
|
94
|
-
New client → building-design-md (brand → DESIGN.md) → softr-vibe-coding (DESIGN.md →
|
|
94
|
+
New client → building-design-md (brand → DESIGN.md) → softr-vibe-coding (DESIGN.md → blocks) → shipped Softr app
|
|
95
95
|
```
|
|
96
96
|
|
|
97
97
|
The upstream skill is [`building-design-md`](https://github.com/leo-softr/design-md-extractor-skill), which extracts a brand foundation (colors, typography, voice) from a website URL or brand guide into a portable `DESIGN.md` file. When that file exists in your project folder, this skill picks it up automatically and applies the brand tokens throughout every block it generates — no re-asking about colors or fonts.
|
|
@@ -152,8 +152,8 @@ Create a contact form that creates records in our Airtable Contacts table
|
|
|
152
152
|
|
|
153
153
|
1. **Asks only what it needs** — data source type and field IDs. Everything else (folder, colors, filename) uses smart defaults.
|
|
154
154
|
2. **Loads the relevant guides** — reads the specific data source guide (Airtable, REST API, etc.) and reference files (helper blocks, Shadow DOM) as needed.
|
|
155
|
-
3. **Generates a complete
|
|
156
|
-
4. **Self-validates** —
|
|
155
|
+
3. **Generates a complete block file** (`.tsx` preferred) — production-ready, visually polished, with loading/error/empty states. Never delivers code inline (prevents JSX character corruption).
|
|
156
|
+
4. **Self-validates** — runs a platform-compatibility checklist (`getFieldValue()` wrapping, hooks ordering, payload shapes, inline hook options) before delivering code.
|
|
157
157
|
|
|
158
158
|
---
|
|
159
159
|
|
|
@@ -163,7 +163,7 @@ Create a contact form that creates records in our Airtable Contacts table
|
|
|
163
163
|
softr-vibe-coding/
|
|
164
164
|
├── SKILL.md # Main skill (330 lines)
|
|
165
165
|
│ # Workflow, code structure, visual baseline,
|
|
166
|
-
│ # components, settings,
|
|
166
|
+
│ # components, settings, 21 hard constraints
|
|
167
167
|
│
|
|
168
168
|
├── ui-ux-guidelines.md # Design reference (746 lines)
|
|
169
169
|
│ # 26 sections: hierarchy, color, typography,
|
|
@@ -179,10 +179,10 @@ softr-vibe-coding/
|
|
|
179
179
|
│ │ # Scripting Extension, cross-table cascades,
|
|
180
180
|
│ │ # batch update gotchas, field-ID discipline,
|
|
181
181
|
│ │ # Airtable formulas
|
|
182
|
-
│ ├── softr-
|
|
183
|
-
│ │ #
|
|
184
|
-
│ │ #
|
|
185
|
-
│ │ #
|
|
182
|
+
│ ├── softr-mcp.md # Official Softr MCP server — vibe coding block
|
|
183
|
+
│ │ # tools (create/edit/version/deploy), integrations
|
|
184
|
+
│ │ # browsing (Airtable/Sheets/Notion/Supabase),
|
|
185
|
+
│ │ # Softr DB schema + record tools, auth, permissions
|
|
186
186
|
│ ├── advanced-integrations.md # Shadow DOM CSS isolation (69 lines)
|
|
187
187
|
│ │ # Leaflet, Mapbox, TinyMCE, Quill, FullCalendar
|
|
188
188
|
│ ├── native-chrome-styling.md # Restyle Softr's native shell (header, footer,
|
|
@@ -202,13 +202,19 @@ softr-vibe-coding/
|
|
|
202
202
|
│ # Imports, hook signatures, mutation shapes,
|
|
203
203
|
│ # field mapping, component skeleton
|
|
204
204
|
│
|
|
205
|
+
├── tools/ # Bundled CLI scripts (run, not read)
|
|
206
|
+
│ ├── get-airtable-base # Full Airtable base schema export (bash + jq)
|
|
207
|
+
│ └── get-softr-database.py # Full Softr DB schema export (Python stdlib)
|
|
208
|
+
│
|
|
205
209
|
└── datasources/ # Data source guides (loaded on demand)
|
|
206
210
|
├── overview.md # Comparison matrix, selection guide
|
|
207
|
-
├── shared-patterns.md # Index → reading, writing, fields
|
|
211
|
+
├── shared-patterns.md # Index → multi-datasource, reading, writing, fields
|
|
212
|
+
├── multi-datasource.md # Several data sources in ONE block: datasource.define(),
|
|
213
|
+
│ # the from: parameter, getting the datasource UUIDs
|
|
208
214
|
├── reading.md # useRecords, filtering, sorting, pagination,
|
|
209
215
|
│ # metrics, charts, current user (198 lines)
|
|
210
|
-
├── writing.md # Mutations,
|
|
211
|
-
│ # cross-table
|
|
216
|
+
├── writing.md # Mutations, sequential write queues, uploads,
|
|
217
|
+
│ # linked record format, cross-table writes
|
|
212
218
|
├── fields.md # getFieldValue(), field type shapes, record
|
|
213
219
|
│ # structure, debug utilities (160 lines)
|
|
214
220
|
├── rest-api.md # useProxyFetch + useQuery (full docs)
|
|
@@ -254,17 +260,21 @@ Only `SKILL.md` loads into Claude's context when the skill triggers (~330 lines)
|
|
|
254
260
|
|
|
255
261
|
---
|
|
256
262
|
|
|
257
|
-
## Key Softr
|
|
263
|
+
## Key Softr Platform Constraints
|
|
258
264
|
|
|
259
|
-
The skill enforces these automatically, but good to know:
|
|
265
|
+
The skill enforces these automatically, but good to know (verified live against the platform, August 2026):
|
|
260
266
|
|
|
261
|
-
-
|
|
267
|
+
- Modern TypeScript compiles — optional chaining, nullish coalescing, arrows, `const`, generics are all fine (the old `var`-only / no-`?.` rules are retired)
|
|
268
|
+
- Data hook options must be **inline object literals** — `useRecords(opts)` with a variable or wrapper fails to compile
|
|
269
|
+
- Create payloads are **flat**; update payloads are `{ recordId, fields: {...} }` — asymmetric by design
|
|
270
|
+
- `mutateAsync` is fully supported — it's the tool for sequential multi-row saves
|
|
271
|
+
- SELECT fields write by option **label string**; linked records write as arrays of record-id strings
|
|
272
|
+
- Every code recompile resets the block's auto-registered Actions to default permissions — tighten permissions after the last redeploy
|
|
262
273
|
- No `import React from 'react'` — use named imports (`import { useState } from "react"`)
|
|
263
|
-
- No arrow functions in JSX callback props — use `function() {}`
|
|
264
274
|
- Must use `export default function Block()`
|
|
265
|
-
- Must wrap layout in `<div className="container py-
|
|
266
|
-
- Only ONE `useRecords` call per block (
|
|
267
|
-
- `fetchNextPage`
|
|
275
|
+
- Must wrap layout in `<div className="container py-0"><div className="content">`
|
|
276
|
+
- Only ONE `useRecords` call per **datasource** — but a block can connect to several sources; declare them with `datasource.define()` and pass `from:` on every hook
|
|
277
|
+
- `fetchNextPage` never in the render body (infinite loop) — call it from an event handler (Load More `onClick`) or a guarded `useEffect`
|
|
268
278
|
- All hooks declared before any conditional `return` — React error #310
|
|
269
279
|
- Every field value rendered in JSX must pass through `getFieldValue()`
|
|
270
280
|
- Mutations use `recordId` (not `id`) and always call `refetch()` in `onSuccess`
|
package/SKILL.md
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: softr-vibe-coding
|
|
3
3
|
description: >
|
|
4
|
-
Generate custom Softr Vibe Coding blocks as complete
|
|
4
|
+
Generate custom Softr Vibe Coding blocks as complete React components (TSX/JSX). Use this skill whenever the user
|
|
5
5
|
mentions Softr, Vibe Coding, Softr blocks, or wants to build a custom UI component for a Softr app.
|
|
6
6
|
Also trigger when the user asks to create cards, lists, forms, dashboards, charts, detail pages,
|
|
7
7
|
or any interactive block intended for Softr — even if they don't say "Vibe Coding" explicitly.
|
|
8
8
|
If the user mentions Softr in the context of building a custom UI component, creating a JSX block,
|
|
9
|
-
or vibe coding, use this skill.
|
|
9
|
+
or vibe coding, use this skill. Do NOT use for extracting brand tokens or producing a DESIGN.md
|
|
10
|
+
(that is the building-design-md skill), or for charts/dashboards with no Softr app involved.
|
|
10
11
|
when_to_use: >
|
|
11
12
|
Triggers on "build me a Softr block", "create a card component", "make a dashboard",
|
|
12
13
|
"vibe code this", "custom block for Softr", "JSX component for Softr app",
|
|
@@ -18,7 +19,7 @@ allowed-tools: Read Write Glob Grep Bash
|
|
|
18
19
|
|
|
19
20
|
# Softr Vibe Coding Block Generator
|
|
20
21
|
|
|
21
|
-
You generate complete, production-ready Softr Vibe Coding blocks as
|
|
22
|
+
You generate complete, production-ready Softr Vibe Coding blocks as TypeScript React files. A Vibe Coding block is a single file with a default-exported React component, compiled by Softr's server and run in the browser inside a Softr app. The current platform compiles TypeScript with modern syntax — optional chaining (`?.`), nullish coalescing (`??`), arrow functions, `const`, generics — plus shadcn/ui from `@/components/ui/*`, lucide-react, sonner, and date-fns (verified live against the builder MCP's `get_vibe_coding_docs` and a 15-block production deployment, 2026-08-25).
|
|
22
23
|
|
|
23
24
|
> **Scope note — blocks vs. native chrome.** A block is page *content*, rendered inside a shadow DOM. Softr's global **header / top bar / nav / dropdown menus** are native chrome (configured in Studio, rendered in the main document) — you **cannot** build or replace them as a block. To restyle them, add CSS to Settings → Custom Code → Code inside header. See [references/native-chrome-styling.md](references/native-chrome-styling.md).
|
|
24
25
|
|
|
@@ -55,14 +56,16 @@ You generate complete, production-ready Softr Vibe Coding blocks as JSX files. A
|
|
|
55
56
|
3. **Apply defaults for the rest, don't ask.** Infer these from context instead of asking:
|
|
56
57
|
- **Project folder**: Derive from the block description (e.g., "partner-portal", "client-dashboard"). If the user has already specified a folder in this session, reuse it.
|
|
57
58
|
- **Brand colors**: Use whatever was chosen in Step 1 — DESIGN.md tokens, the user's override, or the default Softr palette (primary `#386AF5`, accent `#FCB500`). Never silently fall back to defaults.
|
|
58
|
-
- **Filename**: Derive from the block purpose (e.g., `partner-invite.
|
|
59
|
+
- **Filename**: Derive from the block purpose (e.g., `partner-invite.tsx`, `team-directory.tsx`). The user can rename later.
|
|
59
60
|
|
|
60
61
|
4. **Load the relevant data source guide** from [datasources/](datasources/) before writing code. Read the specific guide for the user's data source type.
|
|
61
62
|
|
|
62
|
-
5. **Write the complete `.jsx`
|
|
63
|
+
5. **Write the complete block file** (`.tsx` preferred; `.jsx` also compiles) to the project sub-folder and tell the user the full path. Create the sub-folder if it doesn't exist yet. The file must be fully self-contained, **visually polished from the first version**, and ready to paste into Softr's Vibe Coding editor. Styling is not an afterthought -- it ships in v1. **Never deliver code inline in chat.** Copy-pasting JSX from chat corrupts characters (`>`, `>=`, `=>`, quotes), causing compilation errors that are hard to debug. Always write to a file.
|
|
64
|
+
|
|
65
|
+
**Delivery path:** if the official Softr MCP server is connected with Applications & Forms full access, offer to deploy the block directly after writing the file — `create_vibe_coding_block` (or `update_vibe_coding_block_code` for edits) plus `connect_vibe_coding_block_data_source` to wire the data. The local `.tsx` file stays the source of truth. Remember: every code push resets the block's Action permissions to defaults (Hard Constraint 21), and a block whose data source isn't connected saves fine but errors at page load. Details in [references/softr-mcp.md](references/softr-mcp.md).
|
|
63
66
|
|
|
64
67
|
6. **Self-validate before delivering.** Before presenting the code as complete, verify:
|
|
65
|
-
-
|
|
68
|
+
- Every data hook is called with an **inline options object literal** — `useRecords({ ... })` written through a variable or wrapper function fails to compile (verified live 2026-08-25). Share `q.select` mappings between hooks, never whole options objects
|
|
66
69
|
- All imports use named imports (no `import React from 'react'`)
|
|
67
70
|
- `export default function Block()` is present
|
|
68
71
|
- Container + content wrappers present (`<div className="container py-0"><div className="content">`)
|
|
@@ -74,10 +77,10 @@ You generate complete, production-ready Softr Vibe Coding blocks as JSX files. A
|
|
|
74
77
|
- All hooks declared before any conditional `return` -- prevents React error #310
|
|
75
78
|
- Sub-components (FieldLabel, TextInput, ChipButton, SectionCard, etc.) defined at **module scope**, NOT inside `Block()` -- prevents inputs losing focus after one keystroke (each render creates a new component identity, React unmounts/remounts the `<input>`)
|
|
76
79
|
- When a custom DESIGN.md is in use, brand `fontFamily` (and any non-inherited brand defaults) set as an **inline style on the block's outermost wrapper** `<div>`, not relied on from `custom-code-header.html` -- Vibe Coding blocks render inside a shadow DOM and `html, body` rules don't cross that boundary. Per-element overrides (e.g. Fraunces serif on h1) still set inline at the element.
|
|
77
|
-
- `fetchNextPage` only
|
|
80
|
+
- `fetchNextPage` never called in the render body — only from an event handler (Load More `onClick` with `disabled={isFetching}`, the official pattern) or a guarded `useEffect` (auto-load-all)
|
|
78
81
|
- Mutations use `recordId` (not `id`) and call `refetch()` in `onSuccess`
|
|
79
|
-
- `useRecordUpdate`
|
|
80
|
-
- `
|
|
82
|
+
- `useRecordUpdate` payload is `{ recordId, fields: { ... } }` — nested. `useRecordCreate` payload is **flat** (no `fields` wrapper). The two shapes are asymmetric by design (verified live 2026-08-25)
|
|
83
|
+
- Sequential multi-row saves use `await hook.mutateAsync(...)` per row, in order, with stop-on-failure + retry state — `mutateAsync` is fully supported on the current platform (verified 2026-08-25; the old ".mutate() only" Action-parser rule is gone — see [datasources/writing.md](datasources/writing.md))
|
|
81
84
|
- No hardcoded domains in links -- use relative paths (`/page?recordId=...`)
|
|
82
85
|
|
|
83
86
|
## What to Clarify
|
|
@@ -86,7 +89,7 @@ When the user describes their block, figure out which of these areas apply and a
|
|
|
86
89
|
|
|
87
90
|
- **Data source type**: Is it Airtable, Softr Database, REST API, or another source? This determines the data fetching approach. **Load the relevant data source guide** from the [datasources/](datasources/) directory before writing code.
|
|
88
91
|
- **Data source fields**: For Airtable/Softr Database, you need actual field IDs. For REST APIs, you access the raw API response directly. If the user doesn't know field IDs:
|
|
89
|
-
- For **Softr Database**, the cleanest path is the **Softr
|
|
92
|
+
- For **Softr Database**, the cleanest path is the official **Softr MCP server** — ask whether they have it installed (`claude mcp list` shows it as `softr` or similar). If yes, query schema directly with the MCP tools instead of asking for paste-ins. The same server also browses connected **Airtable, Google Sheets, Notion, and Supabase** integrations down to field level (`list_data_sources` → ... → `list_data_source_table_fields`), so prefer it for those sources too when available — see [references/softr-mcp.md](references/softr-mcp.md). If no MCP, the next-best option for Softr DB is the bundled **`get-softr-database` CLI script** — tell the user to run `python3 ~/.claude/skills/softr-vibe-coding/tools/get-softr-database.py <database_id>` (it prompts for their Softr API key and exports the full schema to `~/Desktop/softr-database-<id>-<timestamp>.json` — Python stdlib only, nothing to install) and paste the resulting JSON into chat. As a final fallback, ask them to paste the `tablespace-with-tables` network response (DevTools -> Network -> filter that string while on Studio's Data tab) — same JSON content, different acquisition path. Optionally tell them they can install the MCP once with `claude mcp add --transport http softr https://mcp.softr.io/mcp` for future sessions. Full MCP details in [references/softr-mcp.md](references/softr-mcp.md); CLI script details in [datasources/softr-database.md](datasources/softr-database.md#bundled-cli-script-get-softr-database); fallback paste-in workflows in [datasources/fields.md](datasources/fields.md#field-inspector-block).
|
|
90
93
|
- For **Airtable**, the most thorough path is the bundled **`get-airtable-base` shell script** — `bash ~/.claude/skills/softr-vibe-coding/tools/get-airtable-base` (requires `jq` — `brew install jq` on macOS). It prompts for Base ID + PAT, then exports the full schema (every table, every field with both `fld...` IDs and column names, relationships, webhooks, interfaces) to a timestamped Desktop folder. The user pastes `02-schema.json` or the combined `00-bundle.json` into chat. For lighter inspection (just a few fields, runtime-only), suggest the Field Inspector block — empty `q.select({})` works for Airtable. CLI script details in [datasources/airtable.md](datasources/airtable.md#bundled-cli-script-get-airtable-base).
|
|
91
94
|
- For other non-Softr-DB sources where empty `q.select({})` works, suggest the Field Inspector block.
|
|
92
95
|
- **Brand colors**: Already resolved in Step 1 (Detect the brand source). Don't re-ask. The brand source is one of:
|
|
@@ -141,7 +144,9 @@ Softr supports 14 data sources. **Before writing any data-fetching code, read th
|
|
|
141
144
|
| SQL Database | [datasources/sql-database.md](datasources/sql-database.md) | `useRecords` + `q.select()` |
|
|
142
145
|
| REST API | [datasources/rest-api.md](datasources/rest-api.md) | `useProxyFetch` + `useQuery` |
|
|
143
146
|
|
|
144
|
-
|
|
147
|
+
**A block can connect to MORE THAN ONE of these at a time.** Declare them with `datasource.define({ alias: "uuid" })` and pass `from: ds.alias` on every data hook — read two tables and write to a third from a single block. Required reading before building anything multi-table: [datasources/multi-datasource.md](datasources/multi-datasource.md). (This replaces the old one-table-per-block limit and the invisible-helper-block workaround.)
|
|
148
|
+
|
|
149
|
+
Shared data patterns, linked directly (read the one the task needs): **reading** records, filtering, sorting, pagination, metrics, charts, current user — [datasources/reading.md](datasources/reading.md); **writing** — mutations, sequential write queues, uploads, field-type write shapes — [datasources/writing.md](datasources/writing.md); **field values** — `getFieldValue()`, field shapes, debug blocks — [datasources/fields.md](datasources/fields.md). ([datasources/shared-patterns.md](datasources/shared-patterns.md) is the thin index of the same set.)
|
|
145
150
|
|
|
146
151
|
For data source comparison and selection guidance, see [datasources/overview.md](datasources/overview.md).
|
|
147
152
|
|
|
@@ -151,13 +156,14 @@ For advanced patterns beyond data fetching, load the relevant reference when the
|
|
|
151
156
|
|
|
152
157
|
| If the task involves... | Load reference |
|
|
153
158
|
|---|---|
|
|
154
|
-
|
|
|
159
|
+
| Reading/writing **several tables from one block** — `datasource.define()`, the `from:` parameter, obtaining the datasource UUIDs (and why Studio's chat invents them) | [datasources/multi-datasource.md](datasources/multi-datasource.md) |
|
|
160
|
+
| Cross-*block* communication, window globals, breadcrumbs, publishing shared computed state. *(Multi-table reads no longer need a helper — use a second datasource.)* | [references/helper-blocks.md](references/helper-blocks.md) |
|
|
155
161
|
| Embedding third-party libraries with their own CSS (Leaflet, Mapbox, TinyMCE, Quill, FullCalendar) | [references/advanced-integrations.md](references/advanced-integrations.md) |
|
|
156
162
|
| Debugging a broken block, checking patterns before delivery, full violation catalog | [references/anti-patterns.md](references/anti-patterns.md) |
|
|
157
163
|
| Quick syntax check — import paths, hook signatures, mutation call shapes, field mapping | [references/quick-reference.md](references/quick-reference.md) |
|
|
158
164
|
| Small reusable patterns — `localStorage` cross-page state, clipboard copy button | [references/common-patterns.md](references/common-patterns.md) |
|
|
159
165
|
| Writing Airtable Automation Scripts / Scripting Extension scripts / Airtable formulas — companion to Softr blocks for cross-table cascades and computed values | [references/airtable-automations.md](references/airtable-automations.md) |
|
|
160
|
-
|
|
|
166
|
+
| The official **Softr MCP server** — schema discovery and record reads for Softr DB, field-level browsing of connected Airtable / Google Sheets / Notion / Supabase integrations, and **creating, editing, versioning, and deploying Vibe Coding blocks directly** (`get_vibe_coding_docs`, `create_vibe_coding_block`, `connect_vibe_coding_block_data_source`, ...) | [references/softr-mcp.md](references/softr-mcp.md) |
|
|
161
167
|
| Restyling Softr's **native shell — header / footer / nav / dropdowns / page background** (not a block; it's Softr chrome, done with global Custom Code CSS): stable selectors vs. hashed classes, floating "island" header+footer, the dropdown blank-space grid fix, the multi-layer page-background stacking, restyle-vs-replace | [references/native-chrome-styling.md](references/native-chrome-styling.md) |
|
|
162
168
|
| Adding a **dynamic date filter or custom filter control to a native List/Grid block** (via a Custom Code Static block, not a Vibe block): drive the block's conditional filter with `{URL_PARAM:…}`, the empty-param "match nothing" wide-range sentinel, inject the control into the filter row and keep it alive across Softr's re-renders | [references/native-block-filters.md](references/native-block-filters.md) |
|
|
163
169
|
|
|
@@ -358,7 +364,7 @@ var askAi = useNavigationSetting({
|
|
|
358
364
|
- `OPEN_CHAT` — opens Softr's AI chat. **No `destination` or `openIn` needed** — it's the cheapest "Ask AI" button to wire up. **GOTCHA:** Softr's AI pulls context from the block that triggered the chat, NOT from the page. If the block has no data source connected, `chat/prepare` returns HTTP 500 ("Failed to prepare AI assistant") even though the chat panel opens. Fix: in Softr Studio, connect the block to whatever data source the AI should read from — even if the block doesn't read or write any records itself, the connection is what gives the AI context. Verified by direct experiment, May 2026: a button-only helper block with no data source caused this exact failure; connecting it to the same table as the main block fixed it without any code change.
|
|
359
365
|
- `OPEN_URL` — opens an external URL. Needs `destination` (the URL) + `openIn` (`"SELF"` | `"TAB"`).
|
|
360
366
|
- `OPEN_PAGE` — navigates to a Softr page in-app. Needs `destination` (page path) + `openIn` (`"SELF"` | `"TAB"` | `"MODAL"`).
|
|
361
|
-
- `TRIGGER_CUSTOM_WORKFLOW` — runs a Softr workflow.
|
|
367
|
+
- `TRIGGER_CUSTOM_WORKFLOW` — runs a Softr workflow. The documented setting shape is `{ action: "TRIGGER_CUSTOM_WORKFLOW" }` with no `destination` (same shape as `OPEN_CHAT`); the builder picks the workflow in the block's settings panel. Not verified live whether a `destination` is also accepted — don't rely on one.
|
|
362
368
|
|
|
363
369
|
When the action navigates to a record-specific page, pass the runtime record id via the `recordId` prop on `<NavigationAction>` (not on the setting) so Softr can resolve dynamic URLs:
|
|
364
370
|
|
|
@@ -443,29 +449,37 @@ Non-negotiable rules enforced by the Softr platform:
|
|
|
443
449
|
7. **No nested arrays in settings** — Use text with separator, split in code.
|
|
444
450
|
8. **Default export required** — `export default function Block()`.
|
|
445
451
|
9. **Container wrapping** — Always wrap in `<div className="container py-0"><div className="content">`. Vertical padding lives on the inner wrapper and depends on block placement (see "Block Placement & Page Spacing").
|
|
446
|
-
10. **
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
452
|
+
10. **Inline options literals for data hooks** — `useRecords` fails to compile when its options
|
|
453
|
+
object is passed through a variable or wrapper function. Build the options object inline at the
|
|
454
|
+
call site; share `q.select` mappings between hooks, not whole options objects. (Same
|
|
455
|
+
static-analysis family as the `q.select()` / `datasource.define()` literal rules. Verified live
|
|
456
|
+
2026-08-25 — hit in a production block whose options came from a wrapper function; the fix was
|
|
457
|
+
changing the wrapper to take the hook's *result* instead.)
|
|
458
|
+
11. **Airtable, Notion, Google Sheets: use field NAMES, not IDs** — `q.select()` values are field names for these three sources; Softr Database and Supabase use field IDs (Supabase = SQL column name). Getting this wrong fails silently: the block compiles and saves, then renders empty. See [datasources/airtable.md](datasources/airtable.md).
|
|
450
459
|
12. **Record fields nested under `fields`** — Access via `record.fields.alias`, not `record.alias`.
|
|
451
|
-
13. **ONE `useRecords` per
|
|
460
|
+
13. **ONE `useRecords` per datasource** — filter client-side rather than issuing several queries against
|
|
461
|
+
the same table. A block CAN connect to multiple data sources and call `useRecords` once per source;
|
|
462
|
+
declare them with `datasource.define()` and pass `from:` on every hook. See
|
|
463
|
+
[datasources/multi-datasource.md](datasources/multi-datasource.md). Multiple `useMetric` calls OK.
|
|
452
464
|
14. **React functional components only** — No class components.
|
|
453
465
|
15. **Do NOT `import React from 'react'`** — Use named imports for hooks.
|
|
454
466
|
16. **No CSS modules or styled-components** — Tailwind only.
|
|
455
467
|
17. **setTimeout for scroll** -- Wrap programmatic scroll in `setTimeout(fn, 0)`.
|
|
456
|
-
18.
|
|
468
|
+
18. **Never call `fetchNextPage` in the render body** -- render calls `fetchNextPage`, which updates data, which triggers re-render, which calls it again: infinite loop. Call it from an event handler (the official Load More pattern: `<button onClick={() => fetchNextPage()} disabled={isFetching}>`) or from a guarded `useEffect` when intentionally auto-loading all pages.
|
|
457
469
|
19. **All hooks before any conditional `return`** -- Hooks must be called in the same order every render. A hook declared after a conditional `return` causes React error #310.
|
|
458
470
|
20. **Relative paths in navigation** -- Use `/page-name?recordId=...`, never hardcoded domains like `app.client.com/page`.
|
|
471
|
+
21. **Recompiles reset Action permissions** -- Every code recompile/redeploy resets the block's
|
|
472
|
+
auto-registered Actions to **default permissions**. Do the Actions-tab permission tightening pass
|
|
473
|
+
only after the LAST redeploy, and re-check it after any future one. Verified live 2026-08-25
|
|
474
|
+
across a 15-block deployment. See [datasources/writing.md](datasources/writing.md#how-actions-work-studios-actions-tab).
|
|
459
475
|
|
|
460
|
-
## Style Conventions
|
|
476
|
+
## Style Conventions
|
|
461
477
|
|
|
462
|
-
The
|
|
478
|
+
**The current platform compiles TypeScript with modern syntax** — optional chaining (`?.`), nullish coalescing (`??`), arrow functions, `const`/`let`, generics — verified live against `get_vibe_coding_docs` and a 15-block production deployment on 2026-08-25. Write new blocks in modern TS (`.tsx`); it matches what Softr's own Studio AI emits.
|
|
463
479
|
|
|
464
|
-
-
|
|
465
|
-
- Prefer `function() {}` over arrow functions in JSX callbacks and component props
|
|
466
|
-
- Field-value helper property priority: `label` -> `name` -> `title`
|
|
480
|
+
History, kept so old guidance elsewhere is recognizable as superseded: until mid-2026 this skill mandated `var` + `function(){}` and banned `?.` / `??` because the then-current bundler failed on them (verified April 2026; a Studio-scaffolded block already contradicted it by July 2026). That platform behavior is gone. Existing var-style blocks remain valid — the compiler accepts both styles — so don't churn a working block just to modernize its syntax, and don't "fix" modern syntax back to var-style when editing.
|
|
467
481
|
|
|
468
|
-
|
|
482
|
+
Still house conventions: the field-value helper is named `getFieldValue()`, with property priority `label` -> `name` -> `title`.
|
|
469
483
|
|
|
470
484
|
## Anti-Patterns Checklist
|
|
471
485
|
|
package/datasources/airtable.md
CHANGED
|
@@ -120,6 +120,7 @@ This script reads only **metadata**, never records. To inspect record contents i
|
|
|
120
120
|
| Number | Yes | |
|
|
121
121
|
| Date | Yes | |
|
|
122
122
|
| Checkbox | Yes | |
|
|
123
|
+
| Rating | Yes | Star field. **Writable** — send a plain integer `0`–`max`; `0` clears it (Airtable treats a cleared rating as empty, so `AVERAGE` formulas skip it). Reads back as a number. Verified by direct experiment, July 2026: a create Action with three `rating` fields derived cleanly and wrote all three. |
|
|
123
124
|
| Single Select | Yes | Returns as `{ label, id }` object. Use `getFieldValue()` helper to extract the label. |
|
|
124
125
|
| Multiple Select | Yes | Array of `{ label, id }` objects |
|
|
125
126
|
| Attachment | Yes | Array of `{ filename, id, type, url }` objects |
|
package/datasources/fields.md
CHANGED
|
@@ -84,7 +84,7 @@ Two throwaway diagnostic blocks you can drop into a page to diagnose data proble
|
|
|
84
84
|
|
|
85
85
|
Use when records load but fields come back empty, or when you're not sure which Field IDs exist on a table.
|
|
86
86
|
|
|
87
|
-
**Important caveat for Softr Database:** `q.select({})` returns record IDs with empty `fields: {}` -- it does NOT dump all fields. Verified by direct experiment, April 2026. Use one of the alternatives below for Softr DB. The empty-select pattern still works for Airtable and other sources where field
|
|
87
|
+
**Important caveat for Softr Database:** `q.select({})` returns record IDs with empty `fields: {}` -- it does NOT dump all fields. Verified by direct experiment, April 2026. Use one of the alternatives below for Softr DB. The empty-select pattern still works for Airtable and other sources where the field keys come back automatically (for Airtable / Notion / Google Sheets those keys are field NAMES — the same names `q.select()` uses; see the name-vs-ID rule in [../references/softr-mcp.md](../references/softr-mcp.md#browsing-integrations-external-data-sources)).
|
|
88
88
|
|
|
89
89
|
For Airtable and other non-Softr-DB sources:
|
|
90
90
|
|
|
@@ -111,7 +111,7 @@ export default function Block() {
|
|
|
111
111
|
|
|
112
112
|
**For Softr Database, find field IDs via:**
|
|
113
113
|
|
|
114
|
-
1. **Softr
|
|
114
|
+
1. **Softr MCP server (recommended for AI-assisted workflows)** -- if you're collaborating with an AI assistant (Claude Code, Claude Desktop, Cursor, ChatGPT, Mistral) to write Vibe Coding blocks, the official Softr MCP server is the cleanest path. The AI calls schema/list-fields tools directly against your workspace and reads back every field's `id`, `name`, `type`, and dropdown option UUIDs -- no copy-paste, no transcription errors. The same server also browses connected Airtable / Google Sheets / Notion / Supabase integrations down to field level. Full setup and permissions in [../references/softr-mcp.md](../references/softr-mcp.md).
|
|
115
115
|
|
|
116
116
|
2. **`get-softr-database` CLI script (bundled, no MCP needed)** -- a Python CLI bundled with this skill at `~/.claude/skills/softr-vibe-coding/tools/get-softr-database.py`. Exports the full schema (every table, every field, all dropdown option UUIDs) to `~/Desktop/softr-database-<id>-<timestamp>.json`. Run with `python3 ~/.claude/skills/softr-vibe-coding/tools/get-softr-database.py <database_id>` (prompts for API key) or set `SOFTR_API_KEY=xxx` env var to skip the prompt. Stdlib only, no `pip install`. Best when you want a portable JSON dump for sharing in chat, archiving, or diffing across schema versions. Full usage in [softr-database.md](softr-database.md#bundled-cli-script-get-softr-database).
|
|
117
117
|
|
|
@@ -119,7 +119,7 @@ export default function Block() {
|
|
|
119
119
|
- Each field's `id`, `name`, `type`, and `options`
|
|
120
120
|
- For dropdown / SELECT fields: the full `choices` array with every option's `id` (UUID), `label`, and `color`
|
|
121
121
|
|
|
122
|
-
Use this when scaffolding a block that needs many field IDs at once, or to
|
|
122
|
+
Use this when scaffolding a block that needs many field IDs at once, or to see a SELECT field's full choice list. (Write payloads take the option **label**, not the UUID — verified 2026-08-25, see [writing.md](writing.md#dropdown--single-select-softr-database) — but the exported labels are exactly what you need for a correct write vocabulary.) **When working with an AI assistant without the MCP installed**, paste this JSON response into the chat -- second-best way to share accurate field IDs and choice lists in one shot.
|
|
123
123
|
|
|
124
124
|
4. **Inline in Studio (one field at a time)** -- in the Data tab, click a field's name to open its edit drawer. The field ID appears next to the "Field name" label (e.g. `ID: 37fts`). Fastest for spot-checking a single field.
|
|
125
125
|
|
|
@@ -182,7 +182,7 @@ export default function Block() {
|
|
|
182
182
|
|
|
183
183
|
### User Inspector Block
|
|
184
184
|
|
|
185
|
-
Use when debugging permissions, user groups, or the `useCurrentUser()` vs `window.__softr_current_user` distinction. Renders both side-by-side as JSON. This catches a common surprise: `userGroups` only lives on the `window.__softr_current_user` object, not on `useCurrentUser()`.
|
|
185
|
+
Use when debugging permissions, user groups, or the `useCurrentUser()` vs `window.__softr_current_user` distinction. Renders both side-by-side as JSON. This catches a common surprise: `userGroups` only lives on the `window.__softr_current_user` object, not on `useCurrentUser()`. (Custom user-record fields, by contrast, ARE available through the hook — `useCurrentUser({ properties: { alias: "FIELD_ID" } })` exposes them under `user.properties`; see [reading.md](reading.md#current-user).)
|
|
186
186
|
|
|
187
187
|
```jsx
|
|
188
188
|
import { useCurrentUser } from "@/lib/user";
|
package/datasources/monday.md
CHANGED
|
@@ -23,7 +23,7 @@ Use the Field Inspector block to determine exact field IDs for your monday.com b
|
|
|
23
23
|
| Dropdown | Yes | |
|
|
24
24
|
| Checkbox | Yes | |
|
|
25
25
|
| Email | Yes | |
|
|
26
|
-
| Phone | Yes | |
|
|
26
|
+
| Phone | Yes | Writes rejected unless value is `+` followed by digits only — no spaces, dashes, or parentheses. See Gotchas |
|
|
27
27
|
| Link | Yes | |
|
|
28
28
|
| People | Yes | |
|
|
29
29
|
| File | Yes | |
|
|
@@ -42,6 +42,13 @@ monday.com enforces API rate limits based on your monday.com plan tier. Softr re
|
|
|
42
42
|
- **API token authentication only.** Uses a Personal API Token from monday.com's admin settings, not OAuth.
|
|
43
43
|
- **Connected Boards** (linked records between boards) are supported but may have limitations depending on the board configuration.
|
|
44
44
|
- **Mirror columns are read-only.** These reflect data from connected boards and cannot be written to from Softr.
|
|
45
|
+
- **Phone writes are strict.** monday.com is the datasource the official Softr guide names as rejecting formatted phone values. Send unformatted international format only (`+` then digits, e.g. `+12125550100`) and sanitize before `mutate()`:
|
|
46
|
+
|
|
47
|
+
```tsx
|
|
48
|
+
const sanitizePhone = (raw: string) => raw.replace(/[^\d+]/g, "").replace(/(?!^)\+/g, "");
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
See [writing.md](writing.md#phone).
|
|
45
52
|
- **Status columns** use monday.com's internal label/index system. Map these appropriately in your block logic for display purposes.
|
|
46
53
|
- **Subitems** may require additional configuration to surface correctly in Softr blocks.
|
|
47
54
|
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
# Multiple Data Sources in One Block
|
|
2
|
+
|
|
3
|
+
A Vibe Coding block can connect to **several data sources at once**. Declare them with
|
|
4
|
+
`datasource.define()` and target one per hook with `from:`.
|
|
5
|
+
|
|
6
|
+
This supersedes the old one-table-per-block limit. Blocks that needed a second table used to
|
|
7
|
+
require an invisible helper block publishing to a `window` global — that workaround is no
|
|
8
|
+
longer necessary for plain multi-table reads. See [../references/helper-blocks.md](../references/helper-blocks.md)
|
|
9
|
+
for what helper blocks are still genuinely for.
|
|
10
|
+
|
|
11
|
+
## The pattern
|
|
12
|
+
|
|
13
|
+
```jsx
|
|
14
|
+
import { datasource, useRecords, useRecordCreate, q } from "@/lib/datasource";
|
|
15
|
+
|
|
16
|
+
var ds = datasource.define({
|
|
17
|
+
people: "74d2cbfd-f2cb-4f5c-82d9-0d3a0651e531",
|
|
18
|
+
shifts: "ec7a6311-f6c3-4c99-881d-aae308148716",
|
|
19
|
+
feedback: "52461ab9-9912-4e15-bcf4-8838d38c64ea",
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
var peopleSelect = q.select({ email: "Email", firstName: "First name" });
|
|
23
|
+
var shiftSelect = q.select({ jobCode: "Job Code" });
|
|
24
|
+
var feedbackCreateFields = q.select({ comments: "Comments", crewMember: "Crew Member" });
|
|
25
|
+
|
|
26
|
+
export default function Block() {
|
|
27
|
+
var people = useRecords({ from: ds.people, select: peopleSelect, count: 20 });
|
|
28
|
+
var shifts = useRecords({ from: ds.shifts, select: shiftSelect, count: 5 });
|
|
29
|
+
|
|
30
|
+
var createFeedback = useRecordCreate({
|
|
31
|
+
from: ds.feedback,
|
|
32
|
+
fields: feedbackCreateFields,
|
|
33
|
+
onSuccess: function () { /* … */ },
|
|
34
|
+
});
|
|
35
|
+
// …
|
|
36
|
+
}
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
**`from:` is required on every data hook once a block has more than one source.** Omitting it
|
|
40
|
+
throws. With exactly one source you can skip `datasource.define` and omit `from` entirely —
|
|
41
|
+
the hooks default to that source.
|
|
42
|
+
|
|
43
|
+
Applies to: `useRecords`, `useRecord`, `useLinkedRecords`, `useFieldOptions`, `useMetric`,
|
|
44
|
+
`useChartData`, `useRecordCreate`, `useRecordUpdate`, `useRecordDelete`.
|
|
45
|
+
|
|
46
|
+
Does **not** apply to `useUpload` and `useCurrentRecordId` — those are app-level and take no `from`.
|
|
47
|
+
|
|
48
|
+
`useProxyFetch` (REST API sources) is **also datasource-scoped**, but takes the alias as its **function argument** rather than a `from:` option — `useProxyFetch(ds.store)`. With a single datasource `useProxyFetch()` works bare; once the block has more than one, omitting the alias throws, exactly like omitting `from:` on a record hook. See [rest-api.md](rest-api.md#multiple-datasources).
|
|
49
|
+
|
|
50
|
+
## The values must be inline string literals
|
|
51
|
+
|
|
52
|
+
Softr statically analyses `datasource.define()`, exactly like `q.select()`. Hoisting the ids
|
|
53
|
+
into constants fails to compile:
|
|
54
|
+
|
|
55
|
+
```jsx
|
|
56
|
+
// WRONG — "datasource.define() object values must be string literals"
|
|
57
|
+
var PEOPLE_DS_ID = "74d2cbfd-…";
|
|
58
|
+
var ds = datasource.define({ people: PEOPLE_DS_ID });
|
|
59
|
+
|
|
60
|
+
// CORRECT — literals, in place
|
|
61
|
+
var ds = datasource.define({ people: "74d2cbfd-…" });
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
The error text is explicit, so this one fails fast rather than silently — but it's an easy
|
|
65
|
+
reflex to hoist "magic strings" into named constants, and that reflex is wrong here.
|
|
66
|
+
|
|
67
|
+
## Getting the datasource ids — ask for CODE, never for a value
|
|
68
|
+
|
|
69
|
+
The id is a plain **UUID**. It is *not* the underlying table id (`tbl…` in Airtable), and not
|
|
70
|
+
the `ds_id_1` shape used as a placeholder in Softr's own developer guide.
|
|
71
|
+
|
|
72
|
+
**Studio's AI chat fabricates these when asked in prose.** Verified 2026-07-22: asked three
|
|
73
|
+
times for the ids of the same three connected tables, it returned three different sets, once
|
|
74
|
+
recycling a previously-mentioned table's uuid for a different table. All three answers were
|
|
75
|
+
confidently worded. None were flagged as uncertain.
|
|
76
|
+
|
|
77
|
+
Ask it to **write code** instead:
|
|
78
|
+
|
|
79
|
+
```
|
|
80
|
+
Write a datasource.define call covering every data source connected to this
|
|
81
|
+
block, plus one useRecords per source. Output code only.
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
Code generation is bound to the block's real connections, so the ids come out correct — the
|
|
85
|
+
same reason Softr's assistant reliably inlines real select options when scaffolding a form but
|
|
86
|
+
invents values when asked to recite one.
|
|
87
|
+
|
|
88
|
+
**Then verify by running it.** The scaffold renders a list per source; if real rows appear
|
|
89
|
+
under each heading, every alias maps to the table you think it does. A wrong uuid fails safe
|
|
90
|
+
(it matches no datasource, so the block errors) — but a *swapped* pair of correct uuids does
|
|
91
|
+
not, and only running it will catch that.
|
|
92
|
+
|
|
93
|
+
## When you still want a helper block
|
|
94
|
+
|
|
95
|
+
Multi-datasource removes the need for helpers as a *data-access* workaround. They remain the
|
|
96
|
+
right tool for:
|
|
97
|
+
|
|
98
|
+
- **Cross-block communication** — one block triggering or feeding another on the same page.
|
|
99
|
+
- **Publishing computed state** — expensive derivations shared by several consumers.
|
|
100
|
+
- **Rich foreign data via `useLinkedRecords`** — that hook still only returns `{id, title}`
|
|
101
|
+
and silently ignores extra fields in `select`. Reading the foreign table directly with its
|
|
102
|
+
own `from:` is now the simpler fix.
|
|
103
|
+
|
|
104
|
+
## Worked example
|
|
105
|
+
|
|
106
|
+
`crew-feedback-form.jsx` — a public feedback form that reads a person from **People** by an
|
|
107
|
+
`email` URL param, resolves a **Shifts** record from a job-code param, and writes a row to
|
|
108
|
+
**Feedback** linking both. One block, three sources, no helpers, no `window` globals.
|
package/datasources/notion.md
CHANGED
|
@@ -10,8 +10,8 @@ Document and database platform used as a data source for Softr. Requires a Basic
|
|
|
10
10
|
4. If a database does not appear in the list: open Notion, click the ellipsis menu (...) on the database page, go to Connections, find Softr, and click Allow.
|
|
11
11
|
5. Multiple Notion workspaces can be connected to a single Softr app.
|
|
12
12
|
|
|
13
|
-
## Vibe Coding Field
|
|
14
|
-
|
|
13
|
+
## Vibe Coding Field References
|
|
14
|
+
`q.select()` values for Notion are the database's **property NAMES**, not internal IDs — same rule as Airtable and Google Sheets (official developer guide). Getting this wrong fails silently: the block compiles and saves, then renders with empty data. Use the Field Inspector block to confirm the exact property names when unsure.
|
|
15
15
|
|
|
16
16
|
## Supported Fields
|
|
17
17
|
|
package/datasources/overview.md
CHANGED
|
@@ -32,7 +32,7 @@
|
|
|
32
32
|
- Data is connected to **dynamic blocks** (List, Grid, Table, Kanban, Chart, Form, etc.) in the Softr Studio.
|
|
33
33
|
- **Multiple data sources** can coexist in a single Softr app, even on the same page.
|
|
34
34
|
- Softr IP addresses to whitelist for secured databases: `3.120.79.212`, `3.123.159.186`, `52.58.246.121`
|
|
35
|
-
- **AI-assisted schema discovery via MCP:**
|
|
35
|
+
- **AI-assisted schema discovery via MCP:** the official Softr MCP server (`https://mcp.softr.io/mcp`) reads schema and field references directly from the workspace -- Softr Databases in full, plus browsable field-level access to connected **Airtable, Google Sheets, Notion, and Supabase** integrations. Other external sources still need the manual workflows in [fields.md](fields.md). See [../references/softr-mcp.md](../references/softr-mcp.md).
|
|
36
36
|
|
|
37
37
|
## User Sync Availability
|
|
38
38
|
|