{"id":16921209,"url":"https://github.com/bdrelling/plotvapor","last_synced_at":"2025-08-17T16:54:03.841Z","repository":{"id":63905215,"uuid":"371179837","full_name":"bdrelling/PlotVapor","owner":"bdrelling","description":"A library for easily bridging Plot-generated HTML into Vapor.","archived":false,"fork":false,"pushed_at":"2022-09-16T09:21:09.000Z","size":61,"stargazers_count":3,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-08-01T23:30:59.172Z","etag":null,"topics":["framework","server-side-swift","swift","vapor"],"latest_commit_sha":null,"homepage":"https://briandrelling.com","language":"Swift","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/bdrelling.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}},"created_at":"2021-05-26T22:06:37.000Z","updated_at":"2022-08-29T17:32:06.000Z","dependencies_parsed_at":"2022-11-28T19:19:22.606Z","dependency_job_id":null,"html_url":"https://github.com/bdrelling/PlotVapor","commit_stats":null,"previous_names":[],"tags_count":17,"template":false,"template_full_name":null,"purl":"pkg:github/bdrelling/PlotVapor","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bdrelling%2FPlotVapor","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bdrelling%2FPlotVapor/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bdrelling%2FPlotVapor/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bdrelling%2FPlotVapor/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/bdrelling","download_url":"https://codeload.github.com/bdrelling/PlotVapor/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bdrelling%2FPlotVapor/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":270877282,"owners_count":24661121,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-08-17T02:00:09.016Z","response_time":129,"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":["framework","server-side-swift","swift","vapor"],"created_at":"2024-10-13T19:50:59.896Z","updated_at":"2025-08-17T16:54:03.802Z","avatar_url":"https://github.com/bdrelling.png","language":"Swift","funding_links":[],"categories":[],"sub_categories":[],"readme":"# PlotVapor\n\n[![CI Status](https://github.com/bdrelling/PlotVapor/actions/workflows/tests.yml/badge.svg)](https://github.com/bdrelling/PlotVapor/actions/workflows/tests.yml)\n[![Latest Release](https://img.shields.io/github/v/tag/bdrelling/PlotVapor?color=blue\u0026label=)](https://github.com/bdrelling/PlotVapor/tags)\n[![Swift Compatibility](https://img.shields.io/endpoint?url=https%3A%2F%2Fswiftpackageindex.com%2Fapi%2Fpackages%2Fbdrelling%2FPlotVapor%2Fbadge%3Ftype%3Dswift-versions\u0026label=)](https://swiftpackageindex.com/bdrelling/PlotVapor)\n[![Platform Compatibility](https://img.shields.io/endpoint?url=https%3A%2F%2Fswiftpackageindex.com%2Fapi%2Fpackages%2Fbdrelling%2FPlotVapor%2Fbadge%3Ftype%3Dplatforms\u0026label=)](https://swiftpackageindex.com/bdrelling/PlotVapor)\n[![License](https://img.shields.io/github/license/bdrelling/PlotVapor?label=)](https://github.com/bdrelling/PlotVapor/blob/main/LICENSE)\n\n**PlotVapor** is a small package that allows easy rendering of [Plot](https://github.com/JohnSundell/Plot)-generated HTML within the  [Vapor](https://github.com/vapor/vapor) server-side Swift web framework.\n\nThis package only adds a small bridging layer that allows `Plot` to hook into `Vapor`, which keeps this library extremely lightweight. Usage of this library mirrors [LeafKit](https://github.com/vapor/leaf-kit)'s [LeafRender](https://github.com/vapor/leaf-kit/blob/main/Sources/LeafKit/LeafRenderer.swift) class for additional familiarity.\n\n\u003e :warning: The code in this library has been provided as-is and is intended primarily as a reference for educational purposes. It may lack the documentation, stability, and/or functionality necessary to support usage in production applications. As such, I _highly_ recommend simply copying files from this codebase directly rather than pulling it in as a dependency to avoid any breaking changes impacting your workflow. Feel free to open an issue/PR or reach out if you have any questions or feedback.\n\n**Before continuing, you should review the [Plot](https://github.com/JohnSundell/Plot) README and have a solid understanding of how the library works.**\n\n## Usage\n\nWe can add a new route for the `/home` path within the `configureRoutes(_:)` method and render a sample `HTML` object that doesn't do anything fancy.\n\n```swift\nfunc configureRoutes(_ app: Application) throws {\n    app.get(\"home\") { req -\u003e EventLoopFuture\u003cView\u003e in\n        let html = HTML(\n            .head(\n                .title(\"My website\"),\n                .stylesheet(\"styles.css\")\n            ),\n            .body(\n                .div(\n                    .h1(\"Hello, world!\"),\n                    .p(\"Writing HTML in Swift is pretty great!\")\n                )\n            )\n        )\n        \n        return req.plot.render(html)\n    }\n}\n```\n\nSince nobody wants a massive routing file in practice, this library also provides the `Page` and `PageTemplate` protocols to help with keeping the codebase organized and maintainable.\n\n### Page\n\nConform to the `Page` protocol to quickly and easily define a page.\n\n```swift\nstruct MyPage: Page {\n    let title = \"My website\"\n    \n    var content: Component {\n        Div {\n            H1(\"Hello, world!\")\n            Paragraph(\"Writing HTML in Swift is pretty great!\")\n        }\n    }\n}\n```\n\nThen, render it wherever you define your routes.\n\n```swift\nfunc configureRoutes(_ app: Application) throws {\n    app.get(\"home\") { req -\u003e EventLoopFuture\u003cView\u003e in\n        return req.plot.render(MyPage())\n    }\n}\n```\n\nThe example above will only render a simple page with no styling, which is impractical for most use cases. As such, when you inevitably need to modify the `\u003chead\u003e` element, simply override the `head` property of `Page`, like so.\n\n_The following example will render an HTML page identical to the first snippet in this README._\n\n```swift\nstruct MyPage: Page {\n    let title = \"My website\"\n    \n    var head: Node\u003cHTML.DocumentContext\u003e {\n        .head(\n            .title(self.title),\n            .stylesheet(\"styles.css\")\n        )\n    }\n    \n    var content: Component {\n        Div {\n            H1(\"Hello, world!\")\n            Paragraph(\"Writing HTML in Swift is pretty great!\")\n        }\n    }\n}\n```\n\nYou can also override the `body` property directly as well, in the event you need to add `\u003cscript\u003e` elements and so on. The default implementation of that property looks like this the following.\n\n```swift\nstruct MyPage: Page {\n    ...\n    \n    var body: Node\u003cHTML.DocumentContext\u003e {\n        .body(\n            .component(page.content)\n        )\n    }\n\n    ...\n}\n\n```\n\n\u003e :warning: The reason the `head`, `body`, and `content` properties are all handled differently is due to the fact that, at time of writing, `Plot` doesn't provide usage of the `Component` syntax for `\u003chead\u003e` and `\u003cbody\u003e` elements. For more information, see the [Components](https://github.com/JohnSundell/Plot#components) section of the `Plot` README.\n\n### PageTemplate\n\nYou'll probably want to include than one page on your site, and you won't want to override the `head` and `body` properties for every single page you create as it will lead to duplicated, hard-to-maintain code.\n\nInstead, you can simply create a reusable `PageTemplate`. The following snippet of `DefaultPageTemplate` is included within `PlotVapor`, primarily to serve as an example.\n\n```swift\npublic struct DefaultPageTemplate: PageTemplate {\n    public static func head(with page: Page) -\u003e Node\u003cHTML.DocumentContext\u003e {\n        .head(\n            .title(page.title)\n        )\n    }\n\n    public static func body(with page: Page) -\u003e Node\u003cHTML.DocumentContext\u003e {\n        .body(\n            .component(page.content)\n        )\n    }\n}\n```\n\nThen, instead of conforming to `Page`, you can conform to `TemplatedPage` and define the `Template` `typealias` like so.\n\n```swift\nstruct MyPage: TemplatedPage {\n    typealias Template = DefaultPageTemplate\n\n    ...\n}\n```\n\nAnd for even _more_ convenience, you can define a global default template like so. With this, you'll only need to override the `Template` `typealias` for pages that use a different template.\n\n```swift\nextension TemplatedPage {\n    typealias Template = MyPageTemplate\n}\n```\n\n## Additional Notes\n\n### Minification\n\nBy default, all `HTML` is minified before it is rendered. To modify this behavior, use the `indentedBy` parameter to pass an `Indentation.Kind`.\n\n```swift\nfunc configureRoutes(_ app: Application) throws {\n    app.get(\"home\") { req -\u003e EventLoopFuture\u003cView\u003e in\n        let html = HTML(\n            // elements, etc.\n        )\n        \n        return req.plot.render(html, indentedBy: .spaces(2))\n    }\n}\n```\n\n### Swift Concurrency\n\n`PlotVapor` is also compatible with Swift 5.5's new async/await functionality.\n\n```swift\nfunc configureRoutes(_ app: Application) throws {\n    app.get(\"home\") { req async throws -\u003e View in\n        let html = HTML(\n            // elements, etc.\n        )\n        \n        return req.plot.render(html, indentedBy: .spaces(2))\n    }\n}\n```\n\nFor more information on using Swift concurrency within Vapor, check out the [Vapor docs](https://docs.vapor.codes/4.0/async/).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbdrelling%2Fplotvapor","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fbdrelling%2Fplotvapor","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbdrelling%2Fplotvapor/lists"}