The Growing File Problem
The booking form works, but look at BookingForm.svelte. It’s getting long. Contact fields, date/time selection, service choices, add-ons, notes, validation — all in one file. As features grow, this becomes unmanageable.
What You’ll Learn
- Recognize when files are too large
- Understand the benefits of components
- Plan component boundaries
The Messy Single-File Reality
Here’s what happens without components:
<!-- filename: src/lib/components/BookingForm.svelte -->
<script>
// 30+ lines of state declarations
let customerName = $state('');
let customerEmail = $state('');
let customerPhone = $state('');
let bookingDate = $state('');
let selectedTime = $state('');
let selectedService = $state('');
let selectedAddOns = $state([]);
let notes = $state('');
let agreeToTerms = $state(false);
// ... more state
// 50+ lines of helper functions
function generateTimeSlots() { /* ... */ }
function formatDate(date) { /* ... */ }
function formatTime(hour, minute) { /* ... */ }
function calculateTotal() { /* ... */ }
function validateEmail(email) { /* ... */ }
// ... more functions
// Services data
const services = [/* 20+ lines */];
const addOns = [/* 10+ lines */];
const timeSlots = generateTimeSlots();
</script>
<!-- 200+ lines of markup -->
<form>
<fieldset>
<legend>Contact Information</legend>
<!-- 30 lines of contact fields -->
</fieldset>
<fieldset>
<legend>Appointment Details</legend>
<!-- 40 lines of date/time -->
</fieldset>
<fieldset>
<legend>Select a Service</legend>
<!-- 50 lines of service cards -->
</fieldset>
<!-- More sections... -->
</form>
<!-- 100+ lines of styles -->
<style>
/* Styles for everything */
</style> This file could easily reach 500+ lines. Finding anything becomes a treasure hunt.
Problems with Large Files
Hard to navigate — Where’s the time slot logic? Scroll, scroll, scroll…
Difficult to test — You can’t test the service card in isolation.
No reuse — Need service cards elsewhere? Copy-paste the code.
Merge conflicts — Multiple developers editing the same file constantly conflict.
Slow comprehension — New team members take longer to understand.
The Component Solution
Break the form into focused pieces:
BookingForm.svelte
├── ContactFields.svelte (name, email, phone)
├── DateTimePicker.svelte (date and time selection)
├── ServiceSelector.svelte (service cards)
│ └── ServiceCard.svelte (individual card)
├── AddOnSelector.svelte (checkbox list)
├── NotesField.svelte (textarea)
└── PriceSummary.svelte (total calculation) Each component is small, focused, and testable.
Benefits of Components
Single responsibility — Each component does one thing well.
<!-- ServiceCard only displays a service -->
<ServiceCard service={lawnMowing} selected={true} /> Reusability — Use the same component in multiple places.
<!-- On the services listing page -->
{#each services as service}
<ServiceCard {service} />
{/each}
<!-- In the booking form -->
{#each services as service}
<ServiceCard {service} selected={selectedService === service.slug} />
{/each} Isolation — Change one component without affecting others.
Testability — Test components individually.
// Test ServiceCard in isolation
test('displays service name', () => {
render(ServiceCard, { props: { service: mockService } });
expect(screen.getByText('Lawn Mowing')).toBeInTheDocument();
}); How to Identify Components
Look for these signals:
Repeated patterns — The same structure appears multiple times.
<!-- This pattern repeats for each service -->
<label class="service-card">
<input type="radio" />
<h3>{service.name}</h3>
<p>{service.description}</p>
<span>${service.price}</span>
</label> → Extract to <ServiceCard />
Logical groupings — Related fields that form a unit.
<!-- Contact info is a logical group -->
<input name="name" />
<input name="email" />
<input name="phone" /> → Extract to <ContactFields />
Complex logic — Sections with their own state and helpers.
<!-- Time selection has its own logic -->
<script>
function generateTimeSlots() { /* complex */ }
function formatTime() { /* complex */ }
</script> → Extract to <TimeSelector />
Reuse potential — Something you might use elsewhere.
<!-- Price badge could appear anywhere -->
<span class="price">${price}</span> → Extract to <PriceBadge />
Component Boundaries for BookIt
Here’s the plan for the booking form:
| Component | Responsibility |
|---|---|
BookingForm | Orchestrates child components, handles submission |
ServiceCard | Displays a single service option |
PriceBadge | Formats and displays prices |
BookingPreview | Shows summary of entered data |
Start small. Extract the most obviously reusable pieces first.
Don’t Over-Engineer
Not everything needs to be a component:
Too granular:
<!-- Overkill -->
<FormLabel text="Email" />
<FormInput type="email" />
<FormError message={emailError} /> Just right:
<!-- A single cohesive field component -->
<TextField
label="Email"
type="email"
bind:value={email}
error={emailError}
/> If you’re creating components with only 5 lines that are used once, you’ve gone too far.
Summary
Large single-file components become hard to maintain. Breaking UI into smaller components improves organization, enables reuse, and makes testing easier. Look for repeated patterns, logical groupings, and complex isolated logic.
Key takeaways:
- Large files are hard to navigate and maintain
- Components enable reuse and isolation
- Look for repeated patterns and logical groupings
- Don’t over-extract — find the right balance
Next Steps
Let’s extract the first component. Continue with Create ServiceCard Component to build a reusable service display.