Skip to content
| Marketplace
Sign in
Visual Studio Code>Programming Languages>implementNew to Visual Studio Code? Get it now.
implement

implement

implementjs

| (0) | Free
Editor support for implement. Completes Div( to Div({}, ), closes nested calls with a comma, and writes out If and ForEach.
Installation
Launch VS Code Quick Open (Ctrl+P), paste the following command, and press enter.
Copied to clipboard
More Info

implement for VS Code

Editor support for implement. It fills in the shape of a component call so you write the interesting part, not the punctuation.

Works in VS Code and Cursor, in TypeScript and JavaScript.

What it does

Type ( after a component and a preselected suggestion appears. Tab accepts it.

You type You get
Div( at statement level Div({}, ⎸)
Span( nested inside another call Span({}, ⎸),
Img( — a void element, no children Img({⎸})
ChevronDown( — a Lucide icon ChevronDown({⎸})
If( If(⎸condition, )
ForEach( ForEach(⎸items, getKey, render)
Svg( Svg(⎸source, {})

Tab again to reach the props braces. implement.snippets.cursor flips that order if you would rather land in the props.

The trailing comma is independent of props: any call nested in another call or an array gets closed off, including a component that takes no arguments. A component with nothing to fill in is offered only when there is a comma to add — otherwise the snippet would type nothing.

Your own components

Components imported from your project are read off their declarations, so the suggestion matches the signature you actually wrote:

Logo(props: SvgProps = {})                    // Logo({⎸})
Link = (props, ...children) => ...            // Link({}, ⎸)
Toaster({ manager, ...props })                // Toaster({⎸})
Typeset(content: string, className?: string)  // trailing comma only
SiteHeader()                                  // trailing comma only

Relative specifiers resolve against the importing file; bare specifiers go through compilerOptions.paths, following extends. Named re-exports are followed. node_modules is never read.

Constructs

If and ForEach are also offered while you type the name, because what you want there is a whole shape rather than a first argument:

You type You get
If If(⎸).Then()
ForEach ForEach(⎸, (item) => ⎸, (item, index) => ⎸)

If the name is not imported yet, the import is added as part of accepting the suggestion — extending an existing @implementjs/core import rather than adding a second statement.

Settings

All settings are language-overridable.

Setting Default
implement.snippets.mode completion completion offers a suggestion (Tab accepts). auto rewrites as you type the paren, with no keypress — experimental, it races auto-closing brackets and coarsens undo. off disables.
implement.snippets.cursor children Where the cursor lands: Div({}, ⎸) or Div({⎸}, ).
implement.snippets.trailingComma true Close a nested call with a comma.
implement.snippets.requireImport false Only match names already imported. When false, element names also match before the import exists, unless the name is declared in the file.
implement.snippets.resolveLocalComponents true Read declarations of components imported from your project.
implement.snippets.constructs true Offer If and ForEach as whole shapes while typing the name.
implement.snippets.extraSources {} Module specifiers whose capitalized exports follow a component convention, for barrels the resolver cannot see through: { "@/lib/ui": "propsChildren" }.

How it decides

implement's components do not share a first argument, so this cannot be a rule like "capitalized identifier, insert {}". Svg(source, props?) takes a string, If(condition, ...) takes a condition, and every Lucide icon takes props with no children at all — ChevronDown({}, ) is a type error.

So src/symbols.ts holds an explicit table, keyed on (module specifier, exported name) rather than on the local name, because the two entry points do not expose the same set — the helpers live only on the root, so If reached through @implementjs/core/elements is not the helper, it is nothing at all.

The local name is not enough for a second reason: an alias keeps the shape of the name it was exported under, so import { Div as Box } still offers Box({}, ).

Two packages get a package-wide rule instead of a list, because they build every component through one factory and are uniform by construction: primitives via createComponent, lucide via createLucideIcon.

The element list in src/elements.ts is generated from @implementjs/core by pnpm generate, and tests/elements.test.ts fails when it drifts — a stale table makes confidently wrong suggestions, which is worse than none.

Known limits

  • Imports are matched with regular expressions rather than parsed, so import * as El from "..." then El.Div( is not recognised, and neither is a component re-exported through export * from "...". Name the barrel in extraSources for that case.
  • Nesting is found with a small hand-written lexer. It handles comments, all three string forms, and ${} inside templates, but it cannot tell a regex literal from division, so brackets inside a regex can produce a stray comma. Turn trailingComma off if that happens.
  • A component assembled in a way the resolver does not recognise — returned from a factory other than createComponent, or built with Object.assign — reads as unknown, and gets the trailing comma but no props.

Development

pnpm --filter implement-vscode build     # bundle to dist/extension.js
pnpm --filter implement-vscode test      # vitest
pnpm --filter implement-vscode check     # tsc --noEmit
pnpm --filter implement-vscode generate  # re-sync the element list from core
pnpm --filter implement-vscode package   # build implement-vscode.vsix

Install a local build:

cursor --install-extension packages/vscode/implement-vscode.vsix

src/analyze.ts holds the decision-making and imports nothing from vscode, so it is tested directly. src/extension.ts is the only file that touches the editor API.

  • Contact us
  • Jobs
  • Privacy
  • Manage cookies
  • Terms of use
  • Trademarks
© 2026 Microsoft