{"id":48206002,"url":"https://github.com/markbang/memos-embed","last_synced_at":"2026-04-04T18:37:21.884Z","repository":{"id":332123734,"uuid":"1132791421","full_name":"markbang/memos-embed","owner":"markbang","description":"Memos Embed — Type‑safe memo card embeds for usememos.com with a ready‑to‑deploy site plus React and Web Component packages.","archived":false,"fork":false,"pushed_at":"2026-03-31T08:47:28.000Z","size":1064,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-03-31T09:07:18.616Z","etag":null,"topics":["memos","usememos"],"latest_commit_sha":null,"homepage":"https://memos-embed.bangwu.top","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/markbang.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":"2026-01-12T13:07:48.000Z","updated_at":"2026-03-31T08:47:29.000Z","dependencies_parsed_at":null,"dependency_job_id":"c0d7a78a-7098-445d-9a4c-b902533b4fb1","html_url":"https://github.com/markbang/memos-embed","commit_stats":null,"previous_names":["markbang/memos-embed"],"tags_count":7,"template":false,"template_full_name":null,"purl":"pkg:github/markbang/memos-embed","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/markbang%2Fmemos-embed","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/markbang%2Fmemos-embed/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/markbang%2Fmemos-embed/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/markbang%2Fmemos-embed/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/markbang","download_url":"https://codeload.github.com/markbang/memos-embed/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/markbang%2Fmemos-embed/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31409406,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-04T10:20:44.708Z","status":"ssl_error","status_checked_at":"2026-04-04T10:20:06.846Z","response_time":60,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"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":["memos","usememos"],"created_at":"2026-04-04T18:37:18.426Z","updated_at":"2026-04-04T18:37:21.876Z","avatar_url":"https://github.com/markbang.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"![Memos Embed Logo](./apps/site/public/android-chrome-512x512.png)\n\n# Memos Embed\n\n[![CI](https://github.com/markbang/memos-embed/actions/workflows/ci.yml/badge.svg)](https://github.com/markbang/memos-embed/actions/workflows/ci.yml)\n[![npm](https://img.shields.io/npm/v/memos-embed.svg)](https://www.npmjs.com/package/memos-embed)\n\nEmbeddable memo cards for Memos, delivered as a website and npm packages.\n\n## Features\n- Rich memo cards with themes, density presets, and extendable design tokens\n- Core HTML renderer for SSR and static-site workflows\n- Batch multi-memo fetching and shared-style list rendering for note digests and weekly roundups\n- Shared memo client for cross-embed request deduping on React and MDX pages\n- React components for single embeds and multi-memo roundups, with optional pre-fetched memo rendering\n- Web Component wrapper with exposed `::part(...)` hooks\n- Iframe embed route for no-build integrations\n- Lightweight markdown support for headings, lists, task lists, quotes, links, and fenced code blocks\n- Attachment previews for images and grouped reaction badges\n- Optional auto-resizing iframe snippets via `postMessage`\n\n## Workspace Layout\n- `apps/site`: TanStack Start website (docs, playground, iframe embeds)\n- `packages/memos-embed`: core API + SSR HTML helpers\n- `packages/memos-embed-react`: React component wrapper\n- `packages/memos-embed-wc`: Web Component wrapper\n\n## Quick Start\n```bash\npnpm install\npnpm dev\n```\n\nOpen:\n- site: `http://localhost:3000`\n- playground: `http://localhost:3000/playground`\n\n## Build\n```bash\npnpm -r build\n```\n\n## Tests\n```bash\npnpm test\npnpm test:artifacts\n```\n\n## Validation\n```bash\npnpm validate\n```\n\n## Git Hooks\nThis repo uses [`prek`](https://github.com/j178/prek) for local Git hooks.\n\n- `pnpm install` auto-installs the configured `pre-commit` and `pre-push` hooks when possible\n- `pre-commit` runs `pnpm check`\n- `pre-push` runs `pnpm validate`\n- to reinstall hooks manually: `pnpm prek:install`\n- to run all configured hooks on demand: `pnpm prek:run`\n\n## Releasing\n- Add a changeset for package changes with `pnpm changeset`\n- The `Publish Packages` workflow versions changed packages directly on `main`, publishes them to npm, then creates a Git tag and a GitHub Release generated by `changelogithub`\n- If your `main` branch blocks workflow pushes or release creation, add a `CHANGESETS_GITHUB_TOKEN` secret with repo write access\n- Set `NPM_TOKEN` so package publishing can authenticate with npm\n\n## Base URL handling\n`baseUrl` accepts any of these forms and normalizes them internally:\n- `https://demo.usememos.com`\n- `https://demo.usememos.com/api`\n- `https://demo.usememos.com/api/v1`\n\nUse whichever shape you already have. The client resolves memo and user requests against `/api/v1` automatically.\n\n## Usage\n### Core package\n```ts\nimport { extendTheme, fetchMemo, renderMemoHtmlSnippet } from 'memos-embed'\n\nconst memo = await fetchMemo({\n  baseUrl: 'https://demo.usememos.com/api/v1',\n  memoId: '1',\n})\n\nconst blogTheme = extendTheme('paper', {\n  radius: 'var(--radius)',\n  fontFamily: 'inherit',\n  tokens: {\n    background: 'var(--card)',\n    foreground: 'var(--card-foreground)',\n    border: 'var(--border)',\n    accent: 'var(--primary)',\n    accentForeground: 'var(--primary-foreground)',\n    mutedForeground: 'var(--muted-foreground)',\n    codeBackground: 'var(--muted)',\n  },\n})\n\nconst html = renderMemoHtmlSnippet(memo, {\n  includeStyles: true,\n  theme: blogTheme,\n  density: 'comfortable',\n  showAttachments: true,\n  showReactions: true,\n  linkTarget: '_blank',\n})\n```\n\n### Multiple memos on one page\n```ts\nimport { fetchMemos, renderMemoListHtmlSnippet } from 'memos-embed'\n\nconst memos = await fetchMemos({\n  baseUrl: 'https://demo.usememos.com/api/v1',\n  memoIds: ['1', '2', '3'],\n})\n\nconst html = renderMemoListHtmlSnippet(memos, {\n  layout: 'stack',\n  gap: '20px',\n  theme: 'paper',\n})\n```\n\n### Shared memo client\n```ts\nimport { createMemoClient } from 'memos-embed'\n\nconst memoClient = createMemoClient()\n```\n\n### React\n```tsx\nimport { MemoEmbed } from '@memos-embed/react'\n\n\u003cMemoEmbed\n  baseUrl=\"https://demo.usememos.com/api/v1\"\n  memoId=\"1\"\n  theme=\"glass\"\n  density=\"compact\"\n  linkTarget=\"_blank\"\n  showAttachments\n  showReactions\n/\u003e\n```\n\n### React with pre-fetched data\n```tsx\nimport { fetchMemo } from 'memos-embed'\nimport { MemoEmbed } from '@memos-embed/react'\n\nconst memo = await fetchMemo({\n  baseUrl: 'https://demo.usememos.com/api/v1',\n  memoId: '1',\n})\n\n\u003cMemoEmbed memo={memo} /\u003e\n```\n\nWhen you pass a pre-fetched `memo`, the React component now renders the full embed HTML during SSR/SSG instead of waiting for hydration.\n\n### React roundup component\n```tsx\nimport { MemoEmbedList } from '@memos-embed/react'\n\n\u003cMemoEmbedList\n  baseUrl=\"https://demo.usememos.com/api/v1\"\n  memoIds={[\"1\", \"2\", \"3\"]}\n  layout=\"stack\"\n  gap=\"20px\"\n  theme=\"paper\"\n/\u003e\n```\n\n### React shared client\n```tsx\nimport { createMemoClient } from 'memos-embed'\nimport { MemoClientProvider, MemoEmbed, MemoEmbedList } from '@memos-embed/react'\n\nconst client = createMemoClient()\n\n\u003cMemoClientProvider client={client}\u003e\n  \u003cMemoEmbed baseUrl=\"https://demo.usememos.com/api/v1\" memoId=\"1\" /\u003e\n  \u003cMemoEmbedList baseUrl=\"https://demo.usememos.com/api/v1\" memoIds={[\"2\", \"3\"]} /\u003e\n\u003c/MemoClientProvider\u003e\n```\n\nUse a shared client when multiple embeds on one page should reuse memo and creator fetches instead of issuing duplicate requests.\n\n### React shared client with pre-fetched data\n```tsx\nimport { createMemoClient, fetchMemo, fetchMemos } from 'memos-embed'\nimport { MemoClientProvider, MemoEmbed, MemoEmbedList } from '@memos-embed/react'\n\nconst client = createMemoClient()\n\nconst [heroMemo, roundupMemos] = await Promise.all([\n  fetchMemo({\n    baseUrl: 'https://demo.usememos.com/api/v1',\n    memoId: '1',\n  }),\n  fetchMemos({\n    baseUrl: 'https://demo.usememos.com/api/v1',\n    memoIds: ['2', '3'],\n  }),\n])\n\n\u003cMemoClientProvider client={client}\u003e\n  \u003cMemoEmbed\n    baseUrl=\"https://demo.usememos.com/api/v1\"\n    memo={heroMemo}\n  /\u003e\n  \u003cMemoEmbedList\n    baseUrl=\"https://demo.usememos.com/api/v1\"\n    memos={roundupMemos}\n  /\u003e\n\u003c/MemoClientProvider\u003e\n```\n\nPassing `memo` or `memos` while a `MemoClientProvider` is active primes the shared client cache, so later embeds for the same ids can reuse already-fetched data. Those pre-fetched props also render immediately in the initial HTML response for SSR/SSG pages.\n\n### Web Component\n```html\n\u003cscript type=\"module\" src=\"https://unpkg.com/@memos-embed/wc@latest/dist/register.js\"\u003e\u003c/script\u003e\n\u003cmemos-embed\n  base-url=\"https://demo.usememos.com/api/v1\"\n  memo-id=\"1\"\n  theme=\"midnight\"\n  link-target=\"_blank\"\n  show-tags=\"true\"\n  show-attachments=\"true\"\n  show-reactions=\"true\"\n\u003e\u003c/memos-embed\u003e\n```\n\n```css\nmemos-embed::part(container) {\n  border-radius: 24px;\n}\n```\n\n### Iframe\n```ts\nimport { renderIframeHtml } from 'memos-embed'\n\nconst iframe = renderIframeHtml({\n  embedBaseUrl: 'https://your-site.com',\n  baseUrl: 'https://demo.usememos.com/api/v1',\n  memoId: '1',\n  height: 240,\n  autoResize: true,\n})\n```\n\n`autoResize` works through a `postMessage` handshake keyed by `frameId`. `renderIframeHtml()` keeps that wiring aligned for you. If you hand-roll the iframe markup, make sure the iframe element `id` matches the `frameId` query param you send to `/embed/:memoId`.\n\nFor pages with multiple embeds, give each iframe a distinct `frameId` to keep resize events scoped correctly.\n\n## Blog integration examples\n- `examples/next-mdx`: Next.js App Router + server-fetched memo data\n- `examples/mdx-components`: reusable MDX component pattern for React-based blogs\n- `examples/astro-blog`: Astro + MDX blog usage\n- `examples/static-html`: iframe and Web Component copy-paste examples for static sites and CMS pages\n\n## Development Notes\n- The site uses source aliases so local changes in `packages/*` show up immediately in `apps/site`\n- The playground keeps its configuration in the URL for easy sharing\n- Package builds are powered by `tsup`; site builds use Vite + TanStack Start\n- CI runs `pnpm validate`, including lint, Biome checks, tests, package-consumer smoke tests, and the site build\n- Releases are managed with Changesets on `main`: the workflow versions packages, commits the release changes, publishes to npm, and creates a Git tag plus a `changelogithub`-powered GitHub Release automatically\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmarkbang%2Fmemos-embed","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmarkbang%2Fmemos-embed","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmarkbang%2Fmemos-embed/lists"}