Skip to content

[RFC] Support for Configurable Island Locations and Glob Patterns #3700

@Kotsiee

Description

@Kotsiee

Currently, Fresh relies on "magic folders" (islands/ or (_islands)) to identify interactive components. While this works for small projects, it creates a "Thin/Fat" model dilemma for large-scale, domain-driven architectures (common in monorepos).

I am currently building a site with multiple sections/sub-sections. My desired structure is:

  auth/
    components/
    islands/           <-- Hidden from Fresh unless nested in routes
    services/
  billing/
    islands/           <-- Not automatically detected

To make this work currently, I have to either squeeze everything into a routes/sub-section/(_islands) folder (which causes over-bundling and performance hits) or move them to a global islands/ folder (which breaks domain encapsulation).

Suggested Solution
I would like the ability to explicitly define island entry points or search patterns within the fresh.config.ts or deno.json. This would allow the compiler to "find" islands regardless of where they live in the file tree.

Proposed Configuration

export default defineConfig({
  plugins: [
    fresh({
      islands: [
        "./islands/**/*.tsx",
        "./sections/**/islands/*.tsx",
        "./shared/ui/**/*.island.tsx" 
      ],
    }),
  ],
});

Alternative Solutions

  • The (_islands) colocation: While helpful, it forces a specific folder name and deep nesting within the routes directory, making the project structure feel "messy" as it grows.
  • Manual Manifesting: Manually editing the fresh.gen.ts, which is fragile and gets overwritten on every save.

Current

routes/
  billing/
    (_islands)/
      PaymentForm.tsx  <-- Must be here to work
      CardIcon.tsx          <-- Forced to be an island (Over-bundling!)
    services/
    billing_page.tsx

Proposed

features/
  billing/
    PaymentForm.island.tsx  <-- Detected by glob
    CardIcon.tsx                    <-- Remains static (Zero JS)
    billing.service.ts
    billing.contract.ts

Benfits

  1. Enabling Domain-Driven Design (DDD)
    Large-scale applications are rarely organized by "technical type" (putting all islands in one bucket). Instead, they are organized by "feature" (Auth, Billing, Dashboard). Current Fresh constraints force developers to break encapsulation by moving interactive logic far away from its related services and contracts. Explicit configuration allows the folder structure to follow the business logic, not the framework's limitations.

  2. Solving the "Fat Island" Performance Bottleneck
    Currently, placing a component in an islands/ or (_islands)/ folder is an "all-or-nothing" choice. If a component lives there, it is bundled for the client.

  3. First-Class Monorepo Support
    In a monorepo setup, interactive UI components often live in a shared workspace (e.g., packages/ui). Currently, importing these into Fresh requires complex workarounds because they aren't in the local islands/ directory. Allowing an array of paths makes shared component libraries "just work."

  4. Reduced "Magic" and Increased Predictability
    Relying on specific folder names like (_islands) is "magic" that can be confusing for new developers (especially those coming from Angular or React backgrounds). Moving this to a configuration file makes the application's boundaries explicit and easier to debug.

  5. Parity with Modern Tooling
    Since Fresh 2.0 is moving toward Vite, developers expect the flexibility that Vite provides. Almost every other modern framework (Astro, Vite-plugin-ssr, etc.) allows for custom entry-point discovery. Adding this keeps Fresh competitive with the ergonomics of the 2026 web ecosystem.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions