smart-connections-component-patterns
Smart Connections Component Patterns
Use this skill when editing or creating Smart Connections UI components under src/components/**, especially when you need to preserve the project’s component lifecycle, listener guards, and env/settings fallbacks.
Critical
- Keep component work inside
src/components/**; do not move UI logic intosrc/main.js, release scripts, or migration files. - Match the existing component lifecycle used in this repo:
build_html(...)→render(...)→post_process(...). - Guard all event attachment with
if (!container._has_listeners)so rerenders do not duplicate handlers. - Prefer existing helpers and patterns from:
src/components/connections-list/v4.jssrc/components/connections-list-item/v3.jssrc/components/connections-view/v3.jssrc/components/connections_codeblock.jssrc/components/lookup/item_view.js
- Keep fallbacks explicit, especially
opts.connections_settings ?? env.connections_lists.settingsor the equivalent env fallback used by nearby components.
Instructions
- Pick the closest reference component before coding.
- For list item UI, start with
src/components/connections-list-item/v3.js. - For list rendering, start with
src/components/connections-list/v4.js. - For main view shells, start with
src/components/connections-view/v3.jsorsrc/components/connections_codeblock.js. - For lookup-style item UI, start with
src/components/lookup/item_view.js. - Verify the target belongs in
src/components/**and notsrc/views/**before proceeding to the next step. - This step uses the project context and existing component files as the source of truth.
- For list item UI, start with
// src/components/connections-list/v4.js
// Start from this exact file when updating list rendering behavior.
- Copy the existing component shape exactly.
- Implement or edit the file with the same trio of methods used elsewhere in this repo:
build_html(...)render(...)post_process(...)
- Use
this.create_doc_fragment(...)when building DOM,this.empty(...)when replacing content, andthis.apply_style_sheet(...)when the component needs CSS. - Keep naming consistent with the local file family, for example
connections_list_v4,connections_list_item_v3, orconnections_view_v3. - Verify the component can render from a fresh fragment before proceeding to the next step.
- This step uses the output from Step 1.
- Implement or edit the file with the same trio of methods used elsewhere in this repo:
// src/components/connections-list-item/v3.js
build_html(opts, env) { return this.create_doc_fragment(); }
render(_state, opts, env) { this.empty(this.el); this.el.append(...); }
post_process(state, opts, env) { /* wire events + behavior */ }
- Wire DOM markers and actions the same way the repo already does.
- Add
data-attributes for anything the UI needs to query later. - Keep click, drag, and context-menu handlers idempotent with
if (!container._has_listeners). - Reuse existing integrations instead of writing new ones:
register_item_dragregister_item_hover_popoveropen_source
- Verify each listener fires once per container and still works after rerender before proceeding to the next step.
- This step uses the output from Step 2.
- Add
const actionEl = container.querySelector('[data-action="open"]');
if (!container._has_listeners) {
container.addEventListener('click', (event) => {
if (actionEl && actionEl.contains(event.target)) {
open_source(event, source, env);
}
});
container._has_listeners = true;
}
-
Thread settings and state through the same fallback chain used in the codebase.
- When a component needs settings, read from explicit options first and then fallback to env state, for example:
opts.connections_settings ?? env.connections_lists.settings
- Keep mutable UI state isolated; derive display state from input parameters, not globals.
- If you need pause/state behavior, mirror the patterns in
src/utils/pause_controls.jsandsrc/utils/connections_list_item_state.js. - Verify the component produces the same output when called with the same inputs before proceeding to the next step.
- This step uses the output from Step 3.
- When a component needs settings, read from explicit options first and then fallback to env state, for example:
-
Keep CSS colocated and applied through the component.
- If the component needs styling, place CSS beside the component in the same family as
src/components/connections_codeblock.css. - Apply styles through
this.apply_style_sheet(...)rather than inline strings. - Do not invent a new global style pipeline for a single component.
- Verify the component still loads and styles correctly in Obsidian before proceeding to the next step.
- This step uses the output from Step 4.
- If the component needs styling, place CSS beside the component in the same family as
-
Validate with the repo’s actual commands before finishing.
- Run
npm run buildafter component changes. - If you changed shared logic used by the component, run
npm test. - If you changed helper behavior that already has targeted tests, run the relevant AVA commands, for example:
npx ava src/utils/format_connections_as_links.test.js src/utils/connections_list_item_state.test.js
- Verify the build is clean before you consider the component done.
- This step uses the output from Step 5.
- Run
Examples
- User says: "Add a new action button to the connections list item."
- Actions taken: Open
src/components/connections-list-item/v3.js, mirror the existingbuild_html/render/post_processstructure, add adata-actionmarker, attach the handler insideif (!container._has_listeners), and fallback toopts.connections_settings ?? env.connections_lists.settingsfor behavior toggles. - Result: The new button renders like the existing list item UI, triggers once per click, and respects the current settings state.
- Actions taken: Open
// src/components/connections-list-item/v3.js
// Example data marker used by tests and event delegation:
container.dataset.action = 'open-item';
- User says: "Edit the connections list component to show an extra label."
- Actions taken: Update
src/components/connections-list/v4.js, keepthis.create_doc_fragment(...)for DOM construction, preservepost_process(...)for wiring, and runnpm run buildto confirm the component still composes correctly. - Result: The list layout changes without breaking rerenders or duplicated listeners.
- Actions taken: Update
// src/components/connections-list/v4.js
const fragment = this.create_doc_fragment(...);
this.empty(this.container);
this.container.append(fragment);
Common Issues
-
If you see
TypeError: Cannot read properties of undefined (reading 'settings'):- Check the component’s settings access path.
- Use the repo pattern
opts.connections_settings ?? env.connections_lists.settings. - Verify the env value is available before proceeding.
-
If you see duplicate click behavior or handlers firing twice:
- Find the listener attachment block in
post_process(...). - Wrap the attachment in
if (!container._has_listeners). - Set the guard flag after wiring listeners, then rerun the component.
- Find the listener attachment block in
-
If you see
TypeError: this.apply_style_sheet is not a function:- Confirm the code is running inside the component instance pattern used in
src/components/**. - Call
this.apply_style_sheet(...)only from the component lifecycle, not from a free function. - Verify the component still uses the same class/helper shape as the nearby reference file.
- Confirm the code is running inside the component instance pattern used in
-
If the component renders in code but never appears in Obsidian:
- Confirm the component key is registered in
smart_env.config.jsundercomponents. - Match the export name and file name to the convention used by the family (
connections_list_v4,connections_list_item_v3,connections_view_v3). - Rebuild with
npm run buildand reload the plugin.
- Confirm the component key is registered in
-
If styles are missing after the component loads:
- Confirm the stylesheet is colocated with the component family, like
src/components/connections_codeblock.css. - Make sure the component calls
this.apply_style_sheet(...). - Verify the CSS file path matches the component file path exactly before proceeding.
- Confirm the stylesheet is colocated with the component family, like
Related skills
smart-connections— for broader Smart Connections workflow routingsmart-connections-view-command-flow— when component work also changes a view shell or command pathsmart-connections-collection-pipeline— when rendering depends on pipeline output shapeobsidian-dev— for wider Obsidian implementation patterns
More from zpankz/obsidian-skills
viva-llm
Use VIVA LLM for multi-provider chat, voice calls, terminal integration, assistants, skills, MCP tools, and agent mode inside Obsidian. Trigger when the user mentions VIVA LLM, voice chat, realtime voice, LLM providers in Obsidian, or vault-integrated AI chat.
1obsidian-plugin-accessibility
Use this skill when building or reviewing Obsidian plugin UI for keyboard access, ARIA labels, screen reader support, focus handling, or mobile touch targets. Accessibility is treated as mandatory, not optional.
1tasks
Create and query tasks using the Tasks plugin syntax including due dates, recurrence, priorities, and task queries. Use when the user mentions Tasks plugin, recurring tasks, task queries, or advanced task management in Obsidian.
1dataview
Create Dataview queries using DQL (Dataview Query Language), inline queries, and DataviewJS. Use when the user mentions Dataview, DQL, querying notes, listing notes by metadata, or creating dynamic views of vault content.
1defuddle
Extract clean markdown from web pages using Defuddle CLI, removing clutter to save tokens. Use when the user provides a URL to read or analyze.
1datacore
Create Datacore views using JSX/React syntax and the dc.* API. Use when the user mentions Datacore, dc.useQuery, JSX views, or React-based vault queries. Datacore is the successor to Dataview with better performance and interactive views.
1