{"id":28613864,"url":"https://github.com/yusukebe/vite-ssr-components","last_synced_at":"2026-04-28T08:04:52.805Z","repository":{"id":294594301,"uuid":"987494721","full_name":"yusukebe/vite-ssr-components","owner":"yusukebe","description":"JSX Components and Vite plugins for SSR apps","archived":false,"fork":false,"pushed_at":"2026-04-28T01:39:29.000Z","size":153,"stargazers_count":63,"open_issues_count":6,"forks_count":2,"subscribers_count":2,"default_branch":"main","last_synced_at":"2026-04-28T03:12:43.695Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"https://www.npmjs.com/package/vite-ssr-components","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/yusukebe.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2025-05-21T06:50:15.000Z","updated_at":"2026-04-28T01:39:33.000Z","dependencies_parsed_at":"2025-06-25T02:24:45.143Z","dependency_job_id":"c87e992f-7c73-49d9-82c2-a2b552a66854","html_url":"https://github.com/yusukebe/vite-ssr-components","commit_stats":null,"previous_names":["yusukebe/vite-ssr-components"],"tags_count":16,"template":false,"template_full_name":null,"purl":"pkg:github/yusukebe/vite-ssr-components","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/yusukebe%2Fvite-ssr-components","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/yusukebe%2Fvite-ssr-components/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/yusukebe%2Fvite-ssr-components/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/yusukebe%2Fvite-ssr-components/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/yusukebe","download_url":"https://codeload.github.com/yusukebe/vite-ssr-components/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/yusukebe%2Fvite-ssr-components/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":32371703,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-27T20:07:02.737Z","status":"online","status_checked_at":"2026-04-28T02:00:07.250Z","response_time":56,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2025-06-12T01:01:55.829Z","updated_at":"2026-04-28T08:04:52.775Z","avatar_url":"https://github.com/yusukebe.png","language":"TypeScript","funding_links":[],"categories":["TypeScript"],"sub_categories":[],"readme":"# vite-ssr-components\n\n**_vite-ssr-components_** provides JSX components and Vite plugins for helping the development and deployment for SSR application with Vite.\n\n## Motivation\n\nWhen using Cloudflare's Vite plugin for SSR applications, several challenges arise:\n\n- **Missing Vite client scripts**: The Vite client script (`/@vite/client`) needs to be manually embedded in server-side rendered HTML\n- **No SSR hot reload**: Server-side code changes don't trigger hot reloads during development\n- **Complex asset path resolution**: Resolving script and asset paths after build requires manual `manifest.json` handling\n- **Manual build configuration**: Manually specifying entry files in `vite.config.ts` for each Script/Link component\n\nThis library solves these issues by providing:\n\n- **ViteClient component** for development mode\n- **SSR hot reload** plugin for seamless development experience\n- **Automatic asset path resolution** using Vite's `manifest.json`\n- **Automatic build entry detection** from Script/Link components with flexible component-attribute mapping\n- **Framework agnostic** components for both hono/jsx and React\n\n## Install\n\n```bash\nnpm i -D vite-ssr-components\n```\n\n## Quick Start\n\n### 1. Add the SSR Plugin\n\n```ts\n// vite.config.ts\nimport { defineConfig } from 'vite'\nimport { cloudflare } from '@cloudflare/vite-plugin'\nimport ssrPlugin from 'vite-ssr-components/plugin'\n\nexport default defineConfig({\n  plugins: [cloudflare(), ssrPlugin()],\n})\n```\n\n### 2. Use Components in Your SSR Code\n\n```tsx\nimport { Script, Link, ViteClient } from 'vite-ssr-components/hono'\n// import { Script, Link, ViteClient } from 'vite-ssr-components/react'\n\nfunction App() {\n  return (\n    \u003chtml\u003e\n      \u003chead\u003e\n        \u003cViteClient /\u003e\n        \u003cScript src='/src/client.tsx' /\u003e\n        \u003cLink href='/src/style.css' rel='stylesheet' /\u003e\n      \u003c/head\u003e\n      \u003cbody\u003e\n        \u003cdiv id='root' /\u003e\n      \u003c/body\u003e\n    \u003c/html\u003e\n  )\n}\n```\n\nThat's it! The plugin automatically:\n\n- Scans your source files for Script/Link components\n- Detects the referenced files from component attributes\n- Adds the detected files to Vite's build input\n- Configures client build settings\n- Enables SSR hot reload\n\n## Components\n\n\u003e [!IMPORTANT]\n\u003e If you define custom components with the same names as the default `Link` and `Script` components, unexpected behavior may occur. In such cases, use different names for your custom components or specify custom component names in the plugin's `components` configuration.\n\n### ViteClient\n\nAdds Vite client script for development mode.\n\n```tsx\nimport { ViteClient } from 'vite-ssr-components/hono'\n// import { ViteClient } from 'vite-ssr-components/react'\n\nfunction App() {\n  return (\n    \u003chtml\u003e\n      \u003chead\u003e\n        \u003cViteClient /\u003e\n      \u003c/head\u003e\n    \u003c/html\u003e\n  )\n}\n// Renders: \u003cscript type=\"module\" src=\"/@vite/client\"\u003e\u003c/script\u003e\n```\n\n### Script\n\nAdds script tags with proper Vite handling. In production mode, automatically reads `manifest.json` to resolve the correct built file paths.\n\n```tsx\nimport { Script } from 'vite-ssr-components/hono'\n// import { Script } from 'vite-ssr-components/react'\n\nfunction App() {\n  return (\n    \u003chtml\u003e\n      \u003chead\u003e\n        \u003cScript src='/src/client.tsx' /\u003e\n      \u003c/head\u003e\n    \u003c/html\u003e\n  )\n}\n// Development: \u003cscript type=\"module\" src=\"/src/client.tsx\"\u003e\u003c/script\u003e\n// Production: \u003cscript type=\"module\" src=\"/assets/client-abc123.js\"\u003e\u003c/script\u003e\n```\n\n#### Options\n\n- `src` (required): Source path of the script file\n- `manifest`: Custom Vite manifest object (auto-loaded if not provided)\n- `prod`: Force production mode (defaults to `import.meta.env.PROD`)\n- `baseUrl`: Base URL for assets (defaults to `/`)\n- All standard HTML script attributes are supported\n\n```tsx\n\u003cScript src='/src/client.tsx' baseUrl='/app/' defer onLoad={() =\u003e console.log('loaded')} /\u003e\n```\n\n### Link\n\nAdds link tags for stylesheets and other resources. In production mode, automatically reads `manifest.json` to resolve the correct built file paths.\n\n```tsx\nimport { Link } from 'vite-ssr-components/hono'\n// import { Link } from 'vite-ssr-components/react'\n\nfunction App() {\n  return (\n    \u003chtml\u003e\n      \u003chead\u003e\n        \u003cLink href='/src/style.css' rel='stylesheet' /\u003e\n      \u003c/head\u003e\n    \u003c/html\u003e\n  )\n}\n// Development: \u003clink href=\"/src/style.css\" rel=\"stylesheet\"\u003e\n// Production: \u003clink href=\"/assets/style-def456.css\" rel=\"stylesheet\"\u003e\n```\n\n#### Options\n\n- `href` (required): Source path of the resource\n- `manifest`: Custom Vite manifest object (auto-loaded if not provided)\n- `prod`: Force production mode (defaults to `import.meta.env.PROD`)\n- `baseUrl`: Base URL for assets (defaults to `/`)\n- All standard HTML link attributes are supported\n\n```tsx\n\u003cLink href='/src/style.css' rel='stylesheet' baseUrl='/app/' media='screen' /\u003e\n```\n\n### ReactRefresh (React only)\n\nEnables React Fast Refresh in development. Requires [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/tree/main/packages/plugin-react) to be installed and configured.\n\n```tsx\nimport { ReactRefresh } from 'vite-ssr-components/react'\n\nfunction App() {\n  return (\n    \u003chtml\u003e\n      \u003chead\u003e\n        \u003cReactRefresh /\u003e\n      \u003c/head\u003e\n    \u003c/html\u003e\n  )\n}\n// Adds React refresh runtime in development mode\n```\n\n## Plugins\n\n### SSR Plugin\n\nThe main plugin that combines auto-entry detection and SSR hot reload functionality.\n\n```ts\n// vite.config.ts\nimport { defineConfig } from 'vite'\nimport ssrPlugin from 'vite-ssr-components/plugin'\n\nexport default defineConfig({\n  plugins: [ssrPlugin()],\n})\n```\n\n#### Options\n\n```ts\ninterface Component {\n  name: string // Component name to detect\n  attribute: string // Attribute name to extract file path from\n}\n\ninterface SSRPluginOptions {\n  entry?: {\n    target?: string | string[] // File patterns to scan (default: '**/*.{tsx,ts}')\n    components?: Component[] // Component configurations (default: [{ name: 'Script', attribute: 'src' }, { name: 'Link', attribute: 'href' }])\n  }\n  hotReload?:\n    | boolean\n    | {\n        target?: string | string[] // File patterns to watch (default: ['**/*.ts', '**/*.tsx'])\n        ignore?: string | string[] // File patterns to ignore\n      }\n}\n```\n\nWhen `target` / `entry` is omitted, both `autoEntry` and `ssrHotReload` scan\nthe whole project. The following directories are excluded automatically:\n\n- **Fixed**: `node_modules`, `dist`, `build`, `out`, `coverage`, plus any\n  dotfile directories\n- **Dynamic**: `build.outDir` and every `environments[*].build.outDir`\n  configured on the resolved Vite config\n\nFor `autoEntry`, files matching the pattern are also fast-skipped before\nthe AST parser runs when none of the configured component names appears as\na substring of the file — this keeps the wider default cheap on monorepos\nand large codebases.\n\n#### Examples\n\n```ts\n// Basic usage\nexport default defineConfig({\n  plugins: [ssrPlugin()],\n})\n\n// Custom file patterns and component configurations\nexport default defineConfig({\n  plugins: [\n    ssrPlugin({\n      entry: {\n        target: ['src/**/*.tsx', 'app/**/*.ts'], // Scan multiple patterns\n        components: [\n          { name: 'Script', attribute: 'src' },\n          { name: 'Link', attribute: 'href' },\n          { name: 'CustomScript', attribute: 'source' },\n          { name: 'CustomLink', attribute: 'url' },\n          { name: 'Foo', attribute: 'bar' },\n          { name: 'DataLoader', attribute: 'dataPath' },\n          { name: 'AssetLoader', attribute: 'assetUrl' },\n        ],\n      },\n      hotReload: {\n        target: ['src/**/*.ts', 'src/**/*.tsx'],\n        ignore: ['src/client/**/*'],\n      },\n    }),\n  ],\n})\n\n// Scan only specific directories\nexport default defineConfig({\n  plugins: [\n    ssrPlugin({\n      entry: {\n        target: 'app/**/*.tsx', // Only scan app directory\n      },\n    }),\n  ],\n})\n\n// Disable hot reload\nexport default defineConfig({\n  plugins: [\n    ssrPlugin({\n      hotReload: false,\n    }),\n  ],\n})\n```\n\n#### Auto-Entry Detection\n\nThe plugin automatically scans your source files to detect Script/Link components and extracts file paths from their attributes. This eliminates the need to manually configure build entries.\n\n**How it works:**\n\n1. **File Scanning**: The plugin scans files matching the specified patterns (default: `**/*.{tsx,ts}`, excluding `node_modules`, `dist`, `build`, `out`, `coverage`, dotfile dirs, and any configured `build.outDir`)\n2. **Substring Prefilter**: Files that don't even mention `vite-ssr-components` are skipped before the AST parser runs\n3. **AST Analysis**: Remaining files are parsed and analyzed to find JSX components\n4. **Import-aware Detection**: Only `\u003cScript\u003e` / `\u003cLink\u003e` (or your custom components) imported from `vite-ssr-components` are picked up. Same-named components from other packages (e.g. `@inertiajs/react`'s `\u003cLink\u003e`, `next/script`) are ignored.\n5. **Attribute Extraction**: File paths are extracted from the specified attributes\n6. **Build Configuration**: Detected files are automatically added to Vite's build input\n\n**Resolving the package**\n\nWhen `vite-ssr-components` is installed normally, the plugin also accepts the\nfollowing two import shapes as belonging to the package:\n\n- Relative imports that resolve to a file inside the on-disk package directory\n  (useful in monorepos that reach into the package via `../../`)\n- Bare specifiers under another name (e.g. `@my/ssr`) whose resolved\n  `package.json` directory matches `vite-ssr-components` — covers workspace\n  links / aliases that point at the same package on disk\n\n**Not supported**\n\nThe following import shapes are **not** detected, because resolving them\nrequires Vite's full resolver pipeline (which isn't available at the point\nwhere auto-entry runs):\n\n- `tsconfig.json` `paths` aliases (e.g. `@/components/Script`)\n- Vite `resolve.alias` entries\n\nIf you rely on either, import the components via the canonical\n`vite-ssr-components/\u003centry\u003e` specifier in the files you want auto-entry to\nscan.\n\n\u003e Detected `src` / `href` values are normalized to project-relative paths, so leading-slash forms like `\u003cScript src=\"/src/client.tsx\" /\u003e` are accepted by both rollup and Vite 8 / rolldown.\n\n**Example:**\n\n```tsx\n// src/pages/home.tsx\nfunction HomePage() {\n  return (\n    \u003chtml\u003e\n      \u003chead\u003e\n        \u003cScript src='/src/client.tsx' /\u003e\n        \u003cLink href='/src/style.css' rel='stylesheet' /\u003e\n      \u003c/head\u003e\n    \u003c/html\u003e\n  )\n}\n\n// src/pages/about.tsx\nfunction AboutPage() {\n  return (\n    \u003chtml\u003e\n      \u003chead\u003e\n        \u003cScript src='/src/about-client.tsx' /\u003e\n        \u003cLink href='/src/about.css' rel='stylesheet' /\u003e\n      \u003c/head\u003e\n    \u003c/html\u003e\n  )\n}\n```\n\nThe plugin will automatically detect and add these files to the build:\n\n- `/src/client.tsx`\n- `/src/style.css`\n- `/src/about-client.tsx`\n- `/src/about.css`\n\n#### Custom Component Detection\n\nThe plugin can detect any custom components with any attribute names:\n\n```tsx\n// Custom components in your SSR code\nfunction App() {\n  return (\n    \u003chtml\u003e\n      \u003chead\u003e\n        \u003cCustomScript source='/src/app.js' /\u003e\n        \u003cCustomLink url='/src/main.css' /\u003e\n        \u003cFoo bar='/src/data.json' /\u003e\n        \u003cDataLoader dataPath='/src/config.json' /\u003e\n        \u003cAssetLoader assetUrl='/src/assets/image.png' /\u003e\n      \u003c/head\u003e\n    \u003c/html\u003e\n  )\n}\n```\n\nThe plugin will automatically:\n\n- Detect `CustomScript` components and extract paths from the `source` attribute\n- Detect `CustomLink` components and extract paths from the `url` attribute\n- Detect `Foo` components and extract paths from the `bar` attribute\n- And so on...\n\nEach component type only looks for its specified attribute, ensuring accurate detection:\n\n```tsx\n// Only the correct attributes are detected for each component\n\u003cScript href=\"/src/wrong.css\" src=\"/src/client.tsx\" /\u003e  // Only 'src' is detected\n\u003cLink src=\"/src/wrong.js\" href=\"/src/style.css\" /\u003e     // Only 'href' is detected\n```\n\n#### File Pattern Matching\n\nThe plugin supports flexible file pattern matching using glob patterns:\n\n```ts\n// Single pattern\npattern: 'src/**/*.tsx'\n\n// Multiple patterns\npattern: ['src/**/*.tsx', 'app/**/*.ts', 'components/**/*.jsx']\n\n// Exclude specific directories\npattern: ['src/**/*.{tsx,ts}', '!src/test/**/*']\n```\n\n**Pattern Examples:**\n\n- `src/**/*.tsx` - All .tsx files in src directory and subdirectories\n- `app/**/*.{ts,tsx}` - All .ts and .tsx files in app directory\n- `**/*.jsx` - All .jsx files in the entire project\n- `!node_modules/**/*` - Exclude node_modules directory\n\n## Examples\n\nSee the [examples](./examples) directory for complete working examples.\n\n### hono/jsx\n\n#### Directory Structure\n\n```\nexamples/hono/\n├── src/\n│   ├── index.tsx      # Server entry point\n│   ├── client.tsx     # Client entry point\n│   └── style.css      # Stylesheet\n├── vite.config.ts     # Vite configuration\n└── package.json\n```\n\n#### vite.config.ts\n\n```ts\nimport { defineConfig } from 'vite'\nimport { cloudflare } from '@cloudflare/vite-plugin'\nimport ssrPlugin from 'vite-ssr-components/plugin'\n\nexport default defineConfig({\n  plugins: [cloudflare(), ssrPlugin()],\n})\n```\n\n#### Server Code\n\n```tsx\nimport { Hono } from 'hono'\nimport { Script, Link, ViteClient } from 'vite-ssr-components/hono'\n\nconst app = new Hono()\n\napp.get('/', (c) =\u003e {\n  return c.html(\n    \u003chtml\u003e\n      \u003chead\u003e\n        \u003cViteClient /\u003e\n        \u003cScript src='/src/client.tsx' /\u003e\n        \u003cLink href='/src/style.css' rel='stylesheet' /\u003e\n      \u003c/head\u003e\n      \u003cbody\u003e\n        \u003cdiv id='root' /\u003e\n      \u003c/body\u003e\n    \u003c/html\u003e\n  )\n})\n```\n\n### React\n\n#### Directory Structure\n\n```\nexamples/react/\n├── src/\n│   ├── index.tsx          # Server entry point\n│   ├── style.css          # Stylesheet\n│   └── client/\n│       ├── index.tsx      # Client entry point\n│       └── app.tsx        # React app component\n├── vite.config.ts         # Vite configuration\n└── package.json\n```\n\n#### vite.config.ts\n\n```ts\nimport { defineConfig } from 'vite'\nimport { cloudflare } from '@cloudflare/vite-plugin'\nimport react from '@vitejs/plugin-react'\nimport ssrPlugin from 'vite-ssr-components/plugin'\n\nexport default defineConfig({\n  plugins: [\n    cloudflare(),\n    ssrPlugin({\n      hotReload: {\n        ignore: ['./src/client/**/*.tsx'],\n      },\n    }),\n    react(),\n  ],\n})\n```\n\n#### Server Code\n\n```tsx\nimport { Hono } from 'hono'\nimport { Script, Link, ViteClient, ReactRefresh } from 'vite-ssr-components/react'\nimport { renderToReadableStream } from 'react-dom/server'\n\nconst app = new Hono()\n\napp.get('/', async (c) =\u003e {\n  c.header('Content-Type', 'text/html')\n  return c.body(\n    await renderToReadableStream(\n      \u003chtml\u003e\n        \u003chead\u003e\n          \u003cViteClient /\u003e\n          \u003cReactRefresh /\u003e\n          \u003cScript src='/src/client/index.tsx' /\u003e\n          \u003cLink href='/src/style.css' rel='stylesheet' /\u003e\n        \u003c/head\u003e\n        \u003cbody\u003e\n          \u003cdiv id='root' /\u003e\n        \u003c/body\u003e\n      \u003c/html\u003e\n    )\n  )\n})\n```\n\n## Author\n\nYusuke Wada \u003chttps://github.com/yusukebe\u003e\n\n## License\n\nMIT\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fyusukebe%2Fvite-ssr-components","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fyusukebe%2Fvite-ssr-components","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fyusukebe%2Fvite-ssr-components/lists"}