{"id":50780921,"url":"https://github.com/doors-dev/doors-starter","last_synced_at":"2026-06-12T03:03:14.538Z","repository":{"id":359817805,"uuid":"1247624755","full_name":"doors-dev/doors-starter","owner":"doors-dev","description":"Doors Starter — A minimal, idiomatic starter project for building reactive web apps with Doors and GoX.","archived":false,"fork":false,"pushed_at":"2026-06-11T12:05:57.000Z","size":26,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-06-11T14:06:55.487Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/doors-dev.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","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-05-23T15:07:44.000Z","updated_at":"2026-06-11T12:06:05.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/doors-dev/doors-starter","commit_stats":null,"previous_names":["doors-dev/doors-starter"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/doors-dev/doors-starter","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/doors-dev%2Fdoors-starter","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/doors-dev%2Fdoors-starter/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/doors-dev%2Fdoors-starter/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/doors-dev%2Fdoors-starter/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/doors-dev","download_url":"https://codeload.github.com/doors-dev/doors-starter/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/doors-dev%2Fdoors-starter/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34226630,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-12T02:00:06.859Z","response_time":109,"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":"2026-06-12T03:03:13.725Z","updated_at":"2026-06-12T03:03:14.537Z","avatar_url":"https://github.com/doors-dev.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Doors Starter\n\nA starter project for [Doors](https://github.com/doors-dev/doors) — a reactive Go web framework built on [GoX](https://github.com/doors-dev/gox).\n\n## Project Structure\n\n```\n.\n├── main.go                       # Entry point: creates the Doors app, attaches middleware, starts HTTP server\n├── app.gox                       # Root component: HTML shell with head elements, nav, router, and footer\n├── go.mod / go.sum               # Go module definition and dependency checksums\n│\n├── path/                         # URL path models — Go structs describing URL shapes\n│   └── root.go                   #   Root path model: \"/\" and \"/about\" variants\n│\n├── components/                   # Reusable UI components shared across pages\n│   ├── nav.gox                   #   Top navigation bar with doors.ALink navigation links\n│   ├── footer.gox                #   Page footer\n│   └── not_found.gox             #   404 fallback page with reactive path display\n│\n├── segments/                     # Self-contained page groups (one per path model)\n│   └── root/                     #   Segment for the \"/\" section\n│       ├── main.gox              #     Routes the Root path model to Landing or About\n│       ├── landing.gox           #     Landing page (\"/\") — includes the counter demo\n│       ├── about.gox             #     About page (\"/about\")\n│       └── counter.gox           #     Interactive counter (Source + Bind demo)\n│\n└── assets/                       # Embedded static files\n    ├── embed.go                  #   Go embed directives exposing Style bytes and Static FS\n    ├── style.css                 #   Application stylesheet\n    └── static/                   #   Raw static files served via UseFS middleware\n        └── ico.png               #     Favicon\n```\n\n\u003e Generated `.x.go` files are auto-managed by GoX and omitted from this tree.\n\n\u003e The `segments/` layout above is a suggested convention, not a strict rule.\n\u003e For larger sections you may want to split further — e.g. separate Go packages per page, or shared `components/` and `drivers/` packages within a segment. Keep the codebase systematically organized in whatever way fits your project.\n\n## How to Extend\n\n### Add a New Page to an Existing Path Model\n\nIf you want a new page under the existing `Root` path model (e.g. `/contact`):\n\n1. Add the variant to `path/root.go`:\n   ```go\n   const (\n       RootLanding RootPage = iota\n       RootAbout\n       RootContact   // new\n   )\n   ```\n   Update the struct tag: `` `path:\"/ | /about | /contact\"` ``\n\n2. Create a new page component in `segments/root/`:\n   ```gox\n   elem Contact(p doors.Source[path.Root]) {\n       \u003ctitle\u003eContact\u003c/title\u003e\n       \u003ch1\u003eContact\u003c/h1\u003e\n   }\n   ```\n\n3. Add a new route match in `segments/root/main.gox`:\n   ```gox\n   doors.RouteMatch(func(p path.Root) bool {\n       return p.Page == path.RootContact\n   }).Source(Contact),\n   ```\n\n4. Link to it in `components/nav.gox`:\n   ```gox\n   \u003cli\u003e~topNavLink{model: path.Root{Page: path.RootContact}, text: \"Contact\"}\u003c/li\u003e\n   ```\n\n### Add a New Path Model (New Section)\n\nTo add an entirely new section like `/blog`, `/blog/:id`:\n\n1. Create `path/blog.go`:\n   ```go\n   package path\n\n   type Blog struct {\n       Section BlogSection `path:\"/blog | /blog/:ID\"`\n       ID      string\n   }\n\n   type BlogSection int\n\n   const (\n       BlogIndex BlogSection = iota\n       BlogPost\n   )\n   ```\n\n2. Create `segments/blog/main.gox`:\n   ```gox\n   package blog\n\n   import (\n       \"github.com/doors-dev/doors\"\n       \"github.com/doors-dev/doors-starter/path\"\n   )\n\n   elem Main(p doors.Source[path.Blog]) {\n       ~(p.Route(\n           doors.RouteMatch(func(p path.Blog) bool {\n               return p.Section == path.BlogIndex\n           }).Source(Index),\n           doors.RouteDerive(func(p path.Blog) (string, bool) {\n               return p.ID, p.Section == path.BlogPost\n           }).Beam(Post),\n       ))\n   }\n   ```\n\n    `RouteDerive` matches when `Section == BlogPost` and derives the ID into a `Beam[string]`. The `Post` component receives just the ID instead of the full Blog source, keeping updates narrower.\n\n3. Create the page components:\n   ```gox\n   // segments/blog/index.gox\n   elem Index(p doors.Source[path.Blog]) {\n       \u003ch1\u003eBlog Index\u003c/h1\u003e\n   }\n\n   // segments/blog/post.gox — receives Beam[string] (the ID)\n   elem Post(id doors.Beam[string]) {\n       \u003ch1\u003ePost: ~(id.Bind(elem(v string) { ~(v) }))\u003c/h1\u003e\n   }\n   ```\n\n4. Add the model to the router in `app.gox`:\n   ```gox\n   \u003c\u003e\n       ~(doors.Route(\n           doors.RouteModel(root.Main),\n           doors.RouteModel(blog.Main),    // new — tried in order\n           doors.RouteLocationDefault(components.NotFound),\n       ))\n   \u003c/\u003e\n   ```\n\n### Add a New Static Asset\n\n- **For a cacheable file** (favicon, images): place it in `assets/static/` — it's served via `UseFS` under `/static/`.\n- **For a managed resource** (stylesheet, JS): embed it as `[]byte` in `assets/embed.go` and use it inline:\n  ```gox\n  \u003cscript src=(assets.MyScript)\u003e\u003c/script\u003e\n  ```\n\n### Use Tailwind CSS\n\nCreate a `style.css` at the project root:\n\n```css\n@import \"tailwindcss\";\n```\n\nBuild it to `assets/style.css` (which is embedded by `assets/embed.go`):\n\n```bash\nnpx @tailwindcss/cli -i ./style.css -o ./assets/style.css\n```\n\nSee [Tailwind CLI installation](https://tailwindcss.com/docs/installation/tailwind-cli) for setup instructions. If using npm, add `node_modules/` to `.gitignore`. A standalone binary is also available (slower but no npm needed).\n\n### Inline Scripts and Styles\n\nIt is perfectly fine to use inline `\u003cscript\u003e` and `\u003cstyle\u003e` tags directly. Doors converts them into loadable, cacheable resources at render time. Inline scripts are wrapped in an anonymous async function with access to `$data`, `$hook`, `$fetch`, `$on`, and `$sys`. See the [JavaScript docs](https://doors.dev/docs/15-javascript) for details.\n\n### GoX Workflow\n\n- **Write** GoX source in `.gox` files — this is where templates and components live.\n- **Use** `.go` files for plain Go logic (path models, embed directives, helpers).\n- **`.x.go` files are auto-generated** — the GoX language server keeps them up to date while you edit `.gox` files. Do not edit them manually.\n- Run `gox fmt` then `gox gen` (fmt first, gen second) to format and regenerate from the command line.\n\n### Running the Project\n\n```bash\ngo run .\n```\n\nOpen [http://localhost:8080](http://localhost:8080).\n\n\u003e **Safari on localhost**: Doors uses a `Secure` session cookie by default, which Safari rejects on plain HTTP. Use `doors.WithConf(doors.Conf{ServerSessionCookieNoSecure: true})` or set up local HTTPS.\n\n\u003e **Local HTTPS**: Use [mkcert](https://github.com/FiloSottile/mkcert) for trusted self-signed certs — `mkcert -install \u0026\u0026 mkcert localhost 127.0.0.1 ::1`.\n\n### File Watching\n\nDoors reloads the page automatically when it detects the server has restarted. For file watching during development, install [wgo](https://github.com/bokwoon95/wgo) and run:\n\n```bash\nwgo -file=.go -file=.css -file=.js -file=.ts go run .\n```\n\nThis restarts the server on file changes and the browser reloads automatically.\n\n\u003e Less useful in agentic development — the agent typically restarts the server explicitly after changes.\n\n## Learn More\n\n- [https://doors.dev/docs](https://doors.dev/docs)\n- [https://doors.dev/tutorial](https://doors.dev/tutorial)\n- [doors repo docs](https://github.com/doors-dev/doors/tree/main/docs)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdoors-dev%2Fdoors-starter","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdoors-dev%2Fdoors-starter","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdoors-dev%2Fdoors-starter/lists"}