Building Advanced Component Scaffolding with lomer-ui CLI in Svelte
Advanced Svelte Development · SvelteKit · TypeScript · Component Architecture · CLI Tooling
There’s a particular kind of developer tax that nobody puts on the job description: the ritual of creating the same folder structure, writing the same boilerplate <script lang="ts"> block, the same barrel export, and the same empty style section — over and over, for every component you add to your project. It’s not difficult. It’s just relentlessly tedious, and tedium is the quiet enemy of code quality. Enter lomer-ui CLI, a Svelte component scaffolding tool designed to eliminate exactly that tax — and, as it turns out, to do quite a bit more than the minimum.
This guide is not a “here’s how to install it” walkthrough. Those exist, they’re fine, and they’ll get you to lomer generate Button in about four minutes. What this guide covers is everything past that first command: advanced configuration, custom template authoring, Svelte 5 rune-compatible output, TypeScript-first conventions, and the architectural decisions that make a component library scale without turning into a liability. If you’re building a production SvelteKit application or an internal UI library, this is where the real value lives.
Why Component Scaffolding Is Not Optional at Scale
Ask any team that has shipped a medium-to-large SvelteKit application how consistent their component structure is, and watch the room get uncomfortable. One developer puts styles in a co-located .css file. Another uses <style> blocks. A third goes rogue with a CSS module. The component names are CamelCase except when they’re not. The index files exist in some components and not others. Nobody made a bad decision; decisions just never got made consistently, because nobody enforced them at the moment of creation.
Component scaffolding tools solve this not by reviewing pull requests or writing documentation that nobody reads, but by making the right structure the path of least resistance. When lomer generate DataTable produces a correctly structured, correctly named, TypeScript-typed component in under two seconds, the incentive to hand-craft a slightly different version evaporates. Convention wins not by mandate but by convenience — which is, incidentally, exactly how successful developer tooling always wins.
The second, less obvious benefit is cognitive load reduction. Decisions about folder structure, barrel exports, prop typing conventions, and style co-location are not intellectually interesting decisions. They’re housekeeping. Every minute spent making them is a minute not spent on the actual problem — the business logic, the interaction design, the accessibility semantics. A well-configured Svelte CLI tool like lomer-ui doesn’t just save time; it returns attention to where it belongs.
Getting lomer-ui Installed and Configured for a Real Project
Installation is the easy part. What matters is configuration, because lomer-ui’s default output is a reasonable starting point, not a final answer. The CLI reads from a lomer.config.js (or lomer.config.ts, if you prefer to keep your tooling consistent) file at the project root, and this is where you embed your team’s conventions once so they apply everywhere automatically.
# Install globally
npm install -g lomer-ui
# Or use it without installing via npx
npx lomer-ui generate ComponentName
# Scaffold your first component with TypeScript and Svelte 5 output
lomer generate Button --ts --svelte5
The lomer.config.js file is where the tool goes from useful to essential. You define your preferred output directory, your default flags, your custom template path, and any post-generation hooks you want to run — like automatically running your linter or updating a component registry file. A sensible base configuration for a SvelteKit project with TypeScript looks like this:
// lomer.config.js
export default {
outputDir: "src/lib/components",
typescript: true,
svelte5: true,
templateDir: "./templates/lomer",
barrelExport: true,
style: "scoped", // "scoped" | "module" | "external"
naming: "PascalCase",
postGenerate: ["eslint --fix", "prettier --write"]
};
With this in place, every developer on the team — regardless of IDE, operating system, or personal preference — generates components that look identical at the structural level. The config file lives in version control, gets reviewed like any other code, and evolves intentionally rather than drifting silently. This is the foundation. Everything else builds on it.
Custom Templates: Teaching lomer-ui Your Team’s Language
The default templates that ship with lomer-ui are intentionally generic. They demonstrate the capability without assuming anything about your architecture. In practice, your project has opinions — about whether props are defined as interfaces or type aliases, whether you include a cy-data attribute for Cypress, whether every component gets a class prop for style overrides. Custom templates let you encode those opinions directly into the scaffolding output.
Templates use Handlebars-style placeholder syntax. The CLI resolves {{componentName}}, {{componentNameKebab}}, {{timestamp}}, and a set of conditional blocks for TypeScript and Svelte 5 variants. A custom component.svelte.hbs template for a team that always includes a class prop and a data attribute might look like this:
<!-- templates/lomer/component.svelte.hbs -->
<script lang="ts">
import type { {{componentName}}Props } from "./{{componentName}}.types";
let {
class: className = "",
children,
...props
}: {{componentName}}Props = $props();
</script>
<div
class="{{componentNameKebab}} {className}"
data-testid="{{componentNameKebab}}"
{...props}
>
{@render children?.()}
</div>
<style>
.{{componentNameKebab}} {
/* Component styles */
}
</style>
Notice that this template already encodes several strong opinions: Svelte 5 rune syntax via $props(), a spread of remaining props for extensibility, a children render prop using {@render}, a data-testid attribute for testing hooks, and a co-located style block with a scoped class. Every component generated from this template will share these properties without anyone having to remember to add them. That’s the point. The lomer-ui custom templates system transforms your team’s accumulated component wisdom into an automated default.
Svelte 5 Component Patterns and What lomer-ui Generates for Them
Svelte 5 is not a minor version bump. The runes system — $state, $derived, $effect, $props — represents a fundamental rethinking of how reactivity is expressed in Svelte, moving from compiler magic implicit in variable declaration to explicit reactive primitives. For component scaffolding, this matters because the boilerplate looks meaningfully different from Svelte 4, and getting it wrong creates a component that compiles but behaves unexpectedly.
When you pass --svelte5 to lomer-ui (or set svelte5: true in your config), the CLI generates the correct rune-based structure from the start. A stateful component with derived values no longer uses let declarations and $: reactive statements — it uses $state() and $derived() explicitly. The generated TypeScript type file accounts for this too, distinguishing between bindable state props and read-only props using Svelte 5’s $bindable() rune where appropriate.
<!-- Generated: Counter.svelte (Svelte 5, TypeScript) -->
<script lang="ts">
import type { CounterProps } from "./Counter.types";
let {
initialCount = 0,
onCountChange,
}: CounterProps = $props();
let count = $state(initialCount);
let doubled = $derived(count * 2);
function increment() {
count++;
onCountChange?.(count);
}
</script>
<button onclick={increment}>
Count: {count} (doubled: {doubled})
</button>
// Generated: Counter.types.ts
export interface CounterProps {
initialCount?: number;
onCountChange?: (count: number) => void;
class?: string;
}
This is not just syntactically correct — it’s architecturally correct for Svelte 5. The event handler uses the new on* prop pattern rather than createEventDispatcher, the state is local and explicit, and the prop interface makes optional props explicit with ? rather than relying on default values alone. When this is what your scaffolding produces by default, new components start life as production-ready Svelte components rather than requiring a retrofit before they can be considered finished.
SvelteKit Component Structure: The Architecture lomer-ui Enforces
There’s a spectrum of opinion on how to organise components in a SvelteKit project, ranging from “flat directory of .svelte files” to elaborate atomic design hierarchies with organisms, molecules, and atoms that require a philosophy degree to navigate. lomer-ui doesn’t take a hard position on the philosophical question, but it does strongly encourage co-location: every component lives in its own folder, named after the component, containing all files related to that component.
The default scaffold for a component called ProductCard produces:
src/lib/components/ProductCard/ProductCard.svelte— the component markup and scriptsrc/lib/components/ProductCard/ProductCard.types.ts— TypeScript interfaces for propssrc/lib/components/ProductCard/ProductCard.test.ts— Vitest/Testing Library stubsrc/lib/components/ProductCard/ProductCard.stories.ts— Storybook story (if--storyflag set)src/lib/components/ProductCard/index.ts— barrel export
The barrel export is what makes this structure genuinely useful rather than merely organised. Because index.ts re-exports the component, every consumer imports from $lib/components/ProductCard — not $lib/components/ProductCard/ProductCard. If you later rename the internal file, split it into sub-components, or add an asynchronous wrapper, the import path for consumers stays identical. That’s the kind of interface stability that separates a component library from a component folder.
lomer-ui also maintains a root-level src/lib/components/index.ts file that aggregates all component barrel exports, enabling the wildcard import pattern — import { Button, ProductCard, DataTable } from '$lib/components' — that makes consuming the library ergonomic. The CLI updates this file on every generation run, which means the developer never has to remember to manually add their new component to the registry. It happens automatically, correctly, every time.
TypeScript Svelte Components: Beyond lang="ts"
Adding lang="ts" to a Svelte component’s script block is table stakes. What separates a TypeScript-aware component from a truly TypeScript-first component is the rigor of its type definitions, the explicitness of its public API, and its compatibility with the type inference that consuming code relies on. lomer-ui’s TypeScript output is designed with this distinction in mind, generating types that are useful rather than merely present.
The generated type files use interfaces rather than type aliases for props, because interfaces produce more useful error messages in VSCode and support declaration merging if you need to extend them. Event handler props are typed with precise signatures rather than Function or (...args: any[]) => void. Slot content — or, in Svelte 5, children and named snippet props — is typed using Snippet from the svelte package, which is the correct import for rune-based snippet typing.
// ProductCard.types.ts — generated with --ts --svelte5
import type { Snippet } from "svelte";
export interface ProductCardProps {
product: Product;
variant?: "default" | "compact" | "featured";
onAddToCart?: (productId: string) => void;
onWishlist?: (productId: string) => void;
children?: Snippet;
footer?: Snippet<[{ price: number; currency: string }]>;
class?: string;
[key: string]: unknown; // allows ...rest spread
}
export interface Product {
id: string;
name: string;
price: number;
currency: string;
imageUrl: string;
available: boolean;
}
The [key: string]: unknown index signature at the bottom enables the ...rest props spread pattern without TypeScript errors, while remaining safer than any. The typed snippet footer?: Snippet<[{ price: number; currency: string }]> documents exactly what data the parent component can expect when rendering the footer slot — something that was previously only communicable through comments or documentation. This is what advanced Svelte development looks like when TypeScript is taken seriously as a design tool rather than a linting overhead.
lomer-ui Advanced Usage: Batch Generation, Hooks, and CI Integration
Generating a single component at a time is the entry point. The advanced usage patterns are what make lomer-ui genuinely valuable in a team environment over the long term. Batch generation, post-generation hooks, and CI pipeline integration transform the tool from a developer convenience into an architectural enforcement mechanism.
Batch generation via a manifest file is particularly useful when bootstrapping a new project or migrating an existing codebase to a consistent structure. You define a lomer.manifest.json file listing every component to generate, with its specific flags and template overrides, then run lomer generate --from-manifest to produce the entire component tree in a single command. This is how you bootstrap a design system implementation in hours rather than days, and it’s how you ensure that the twentieth component generated has identical structural conventions to the first.
// lomer.manifest.json
{
"components": [
{ "name": "Button", "variants": ["primary", "secondary", "ghost"] },
{ "name": "Input", "template": "form-field" },
{ "name": "Modal", "flags": ["--async", "--story"] },
{ "name": "DataTable", "flags": ["--story", "--complex"] },
{ "name": "Tooltip" }
]
}
Post-generation hooks execute shell commands after each component is created. The practical applications are substantial: automatically running ESLint and Prettier on the new files eliminates the “I forgot to format before committing” problem entirely. A hook that runs svelte-check catches TypeScript errors in the generated file before the developer even opens it. A hook that commits the new component to Git with a conventional commit message keeps the project history clean without requiring developer discipline. In CI, you can run lomer validate — a command that checks your existing components against your configured templates and flags structural drift — making component architecture a pipeline check rather than a code review concern.
Svelte Component Best Practices That lomer-ui Encodes by Default
The best linting rule is one that fires before the code is written — or, better still, one that makes the wrong approach impossible in the first place. lomer-ui’s generated output embeds a set of Svelte component best practices that the Svelte core team and community have converged on through years of building production applications. Understanding why these conventions exist helps you configure the tool correctly rather than working around defaults you don’t understand.
Prop spreading and rest props are generated by default because they’re essential for composability. A Button component that doesn’t forward aria-*, data-*, and native event attributes to its underlying <button> element is an accessibility and integration liability. The {...props} spread in the generated template isn’t laziness — it’s a deliberate architectural decision that makes the component a transparent wrapper rather than an opaque box. Every HTML attribute that a consumer might need to set should flow through automatically.
The class prop pattern — accepting an optional class string and appending it to the component’s own class string — is essential for theming flexibility. Hard-coding a component’s visual appearance without a class override mechanism means that every design system customisation requires either a prop explosion or a fork of the component. The generated pattern class="base-class {className}" is simple, composable with Tailwind or any utility CSS system, and doesn’t require a separate “className” or “extraClasses” prop that confuses consumers. This is a small thing that saves large arguments in design review.
Separation of props from internal state in the generated scaffold — with a dedicated types file rather than inline type definitions — has a less obvious benefit: it makes the component’s public API independently documentable and importable. Consumers can import just the type to build wrapper components or to type function arguments without importing the component itself. It also makes the .svelte file cleaner and the type definitions easier to review in pull requests, since the type changes and the implementation changes are visually separated.
Setting Up a Svelte Component Library with lomer-ui as the Foundation
A component library is a fundamentally different artifact from an application’s component folder, even if the individual components look similar. A library has external consumers, versioning contracts, bundle size budgets, and documentation requirements that an internal src/lib folder doesn’t. lomer-ui can serve both contexts, but the configuration is meaningfully different, and getting it right from the start prevents painful migrations later.
For a publishable component library built with SvelteKit’s library mode (the package directory structure), lomer-ui should be configured to output into src/lib with barrelExport: true and a root aggregator. Every generated component automatically becomes part of the library’s public API via the root index.ts, which SvelteKit’s svelte-package tool then uses to build the distributable package. The discipline of having lomer-ui manage this aggregation file means you never accidentally publish a component that wasn’t intentionally included, and you never forget to include one that was.
Documentation generation is where the custom template system gets genuinely clever. A template that includes a structured JSDoc comment block — with @component, @prop and @example annotations — means that documentation is scaffolded at the same time as the component, under the same incentive structure that makes other scaffolded elements reliable. Developers fill in the JSDoc because it’s already there and already structured; they would skip it if they had to create it from scratch. This is how you get a component library with consistent, accurate documentation: not through documentation sprints or review checklists, but by making documentation the natural next step after component creation.
Semantic Keyword Reference
The following terms form the semantic core of this article’s topic coverage:
Svelte component scaffolding
SvelteKit component structure
TypeScript Svelte components
Svelte 5 component patterns
Svelte component generator
lomer-ui advanced usage
lomer-ui custom templates
Svelte CLI tools
component scaffolding tool
Svelte component architecture
production-ready Svelte components
Svelte component best practices
advanced Svelte development
Svelte component library setup
$props rune
$state rune
$derived rune
barrel exports Svelte
Svelte slot patterns
Svelte TypeScript boilerplate
component-driven development
reusable UI components
declarative component API
frontend scaffolding automation
Svelte Snippet type
SvelteKit library mode
svelte-package
Vitest Svelte
Storybook Svelte 5
Frequently Asked Questions
How do I use lomer-ui CLI to generate Svelte components?
Install lomer-ui globally with npm install -g lomer-ui, then run lomer generate ComponentName inside your SvelteKit project. The CLI scaffolds a fully structured component folder containing a .svelte file, a TypeScript types file, a test stub, and an index.ts barrel export. Pass --ts for explicit TypeScript mode and --svelte5 to generate rune-based syntax. Persist your preferred flags in lomer.config.js so every team member generates consistently without specifying flags manually.
Can I use custom templates with lomer-ui CLI?
Yes — and this is where lomer-ui moves from convenient to essential. Create a template directory (e.g., ./templates/lomer/) containing .hbs files for each generated artifact. Templates use Handlebars-style placeholders like {{componentName}} and {{componentNameKebab}}, with conditional blocks for TypeScript and Svelte 5 variants. Reference your template directory via the --template flag or the templateDir key in lomer.config.js. Custom templates are the mechanism for encoding your team’s accumulated component conventions into the scaffolding system itself.
Is lomer-ui CLI compatible with Svelte 5 and TypeScript?
Fully compatible. Pass --svelte5 to generate components using Svelte 5 rune syntax — $props(), $state(), $derived(), {@render}, and the Snippet type for typed slot replacements. Combine with --ts to emit a separate TypeScript interfaces file with precise prop and event handler types. Both flags can be set as defaults in lomer.config.js so all generated components automatically use Svelte 5 and TypeScript without any per-run flags.





