{"id":13496770,"url":"https://github.com/BinaryBirds/swift-html","last_synced_at":"2025-03-28T19:30:58.840Z","repository":{"id":39611056,"uuid":"387476473","full_name":"BinaryBirds/swift-html","owner":"BinaryBirds","description":"An awesome Swift HTML DSL library using result builders.","archived":false,"fork":false,"pushed_at":"2024-02-25T00:02:05.000Z","size":346,"stargazers_count":309,"open_issues_count":2,"forks_count":26,"subscribers_count":11,"default_branch":"main","last_synced_at":"2025-03-28T11:33:52.162Z","etag":null,"topics":["dsl","html","result-builder","swift","swift-5","swift-html","swift-package-manager"],"latest_commit_sha":null,"homepage":"","language":"Swift","has_issues":false,"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/BinaryBirds.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}},"created_at":"2021-07-19T13:36:40.000Z","updated_at":"2025-03-05T15:54:58.000Z","dependencies_parsed_at":"2024-02-24T20:23:53.237Z","dependency_job_id":"f6971507-3526-41e1-a43a-75cb0d3f7aa8","html_url":"https://github.com/BinaryBirds/swift-html","commit_stats":null,"previous_names":[],"tags_count":44,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/BinaryBirds%2Fswift-html","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/BinaryBirds%2Fswift-html/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/BinaryBirds%2Fswift-html/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/BinaryBirds%2Fswift-html/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/BinaryBirds","download_url":"https://codeload.github.com/BinaryBirds/swift-html/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":246088400,"owners_count":20721679,"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","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":["dsl","html","result-builder","swift","swift-5","swift-html","swift-package-manager"],"created_at":"2024-07-31T19:01:59.285Z","updated_at":"2025-03-28T19:30:58.444Z","avatar_url":"https://github.com/BinaryBirds.png","language":"Swift","readme":"# SwiftHtml\n\nAn awesome Swift HTML DSL library using result builders.\n\n```swift\nimport SwiftHtml \n\nlet doc = Document(.html) {\n    Html {\n        Head {\n            Title(\"Hello Swift HTML DSL\")\n            \n            Meta().charset(\"utf-8\")\n            Meta().name(.viewport).content(\"width=device-width, initial-scale=1\")\n\n            Link(rel: .stylesheet).href(\"./css/style.css\")\n        }\n        Body {\n            Main {\n                Div {\n                    Section {\n                        Img(src: \"./images/swift.png\", alt: \"Swift Logo\")\n                            .title(\"Picture of the Swift Logo\")\n                        H1(\"Lorem ipsum\")\n                            .class(\"red\")\n                        P(\"Lorem ipsum dolor sit amet, consectetur adipiscing elit.\")\n                            .class([\"green\", \"blue\"])\n                            .spellcheck(false)\n                    }\n\n                    A(\"Download SwiftHtml now!\")\n                        .href(\"https://github.com/binarybirds/swift-html/\")\n                        .target(.blank)\n                        .download()\n                        \n                    Abbr(\"WTFPL\")\n                        .title(\"Do What The Fuck You Want To Public License\")\n                }\n            }\n            .class(\"container\")\n\n            Script().src(\"./js/main.js\").async()\n        }\n    }\n}\n\nlet html = DocumentRenderer(minify: false, indent: 2).render(doc)\nprint(html)\n```\n\n\n## Install\n\nYou can simply use `SwiftHtml` as a dependency via the Swift Package Manager:\n\n```swift\n.package(url: \"https://github.com/binarybirds/swift-html\", from: \"1.6.0\"),\n```\n\nAdd the `SwiftHtml` product from the `swift-html` package as a dependency to your target:\n\n```swift\n.product(name: \"SwiftHtml\", package: \"swift-html\"),\n```\n\nImport the framework:\n\n```swift\nimport SwiftHtml\n```\n\nThat's it.\n\n\n## Creating custom tags\n\nYou can define your own custom tags by subclassing the `Tag` or `EmptyTag` class. \n\nYou can follow the same pattern if you take a look at the core tags.\n\n```swift\nopen class Div: Tag {\n\n}\n\n// \u003cdiv\u003e\u003c/div\u003e - standard tag\n\nopen class Br: EmptyTag {\n    \n}\n// \u003cbr\u003e - no closing tag\n\n```\n\nBy default the name of the tag is automatically derived from the class name (lowercased), but you can also create your own tag type \u0026 name by overriding the `createNode()` class function.\n\n```swift\nopen class LastBuildDate: Tag {\n\n    open override class func createNode() -\u003e Node {\n        Node(type: .standard, name: \"lastBuildDate\")\n    }\n}\n\n// \u003clastBuildDate\u003e\u003c/lastBuildDate\u003e - standard tag with custom name\n```\n\nIt is also possible to create tags with altered content or default attributes.\n\n```swift\nopen class Description: Tag {\n    \n    public init(_ contents: String) {\n        super.init()\n        setContents(\"\u003c![CDATA[\" + contents + \"]]\u003e\")\n    }\n}\n// \u003cdescription\u003e\u003c![CDATA[lorem ipsum]]\u003e\u003c/description\u003e - content wrapped in CDATA\n\nopen class Rss: Tag {\n    \n    public init(@TagBuilder _ builder: () -\u003e Tag) {\n        super.init(builder())\n        setAttributes([\n            .init(key: \"version\", value: \"2.0\"),\n        ])\n    }\n}\n// \u003crss version=\"2.0\"\u003e...\u003c/rss\u003e - tag with a default attribute\n```\n\n## Attribute management\n\nYou can set, add or delete the attributes of a given tag.\n\n```swift\nLeaf(\"example\")\n    // set (override) the current attributes\n    .setAttributes([\n        .init(key: \"a\", value: \"foo\"),\n        .init(key: \"b\", value: \"bar\"),\n        .init(key: \"c\", value: \"baz\"),\n    ])\n    // add a new attribute using a key \u0026 value \n    .attribute(\"foo\", \"example\")\n    // add a new flag attribute (without a value)\n    .flagAttribute(\"bar\")\n    // delete an attribute by using a key\n    .deleteAttribute(\"b\")\n    \n// \u003cleaf a=\"foo\" c=\"baz\" foo=\"example\" bar\u003e\u003c/leaf\u003e\n```\n\nYou can also manage the class atrribute through helper methods.\n\n```swift\nSpan(\"foo\")\n    // set (override) class values \n    .class(\"a\", \"b\", \"c\")\n    // add new class values   \n    .class(add: [\"d\", \"e\", \"f\"])\n    // add new class value if the condition is true\n    .class(add: \"b\", true)\n    /// remove multiple class values\n    .class(remove: [\"b\", \"c\", \"d\"])\n    /// remove a class value if the condition is true\n    .class(remove: \"e\", true)\n\n// \u003cspan class=\"a f\"\u003e\u003c/span\u003e\n```\n\nYou can create your own attribute modifier via an extension.\n\n```swift\npublic extension Guid {\n    \n    func isPermalink(_ value: Bool = true) -\u003e Self {\n        attribute(\"isPermalink\", String(value))\n    }\n}\n```\n\nThere are other built-in type-safe attribute modifiers available on tags.\n\n\n## Composing tags\n\nYou can come up with your own `Tag` composition system by introducing a new protocol.\n\n```swift\nprotocol TagRepresentable {\n\n    @TagBuilder\n    func build() -\u003e Tag\n}\n\nstruct ListComponent: TagRepresentable {\n\n    let items: [String]\n    \n    init(_ items: [String]) {\n        self.items = items\n    }\n\n    func build() -\u003e Tag {\n        Ul {\n            for item in items {\n                Li(item)\n            }\n        }\n    }\n}\n\nlet tag = ListComponent([\"a\", \"b\", \"c\"]).build()\n```\n\nThis way it is also possible to extend the `TagBuilder` to support the new protocol.\n\n```swift\nextension TagBuilder {\n\n    static func buildExpression(_ expression: Tag) -\u003e Tag {\n        expression\n    }\n    \n    static func buildExpression(_ expression: TagRepresentable) -\u003e Tag {\n        expression.build()\n    }\n}\n```\n\nSometimes you'll need extra parameters for the build function, so you have to call the build method by hand.\n\nIn those cases it is recommended to introduce a `render` function instead of using build.\n\n```swift\n\nlet tag = WebIndexTemplate(ctx) {\n    ListComponent([\"a\", \"b\", \"c\"])\n        .render(req)\n}\n.render(req)\n```\n\nIf you want to create a lightweight template engine for the [Vapor](https://vapor.codes/) web framework using SwiftHtml, you can see a working example inside the [Feather CMS core](https://github.com/FeatherCMS/feather-core) repository. \n\n\n## Credits \u0026 references\n\n- [HTML Reference](https://www.w3schools.com/tags/default.asp)\n","funding_links":[],"categories":["Swift"],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FBinaryBirds%2Fswift-html","html_url":"https://awesome.ecosyste.ms/projects/github.com%2FBinaryBirds%2Fswift-html","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FBinaryBirds%2Fswift-html/lists"}