Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

To load CSS that is already in a Go string, adapt the string to the input type your library expects. For an API that accepts io.Reader, use strings.NewReader(cssText) or bytes.NewBufferString(cssText); for a parser that accepts strings directly, pass the string unchanged. You do not need to write the CSS to a temporary file.

The important distinction is what “load” means. Parsing CSS produces tokens or a stylesheet representation. It does not fetch linked stylesheets, apply rules to a browser DOM, or render a page. Those are separate operations.

Choose the input method that matches your Go API

Approach Input Output or purpose Best fit
tdewolff/parse/v2/css io.Reader through parse.NewInput CSS grammar and token iteration Lexing, validation, transformation, or custom processing
aymerick/douceur/parser String Parsed stylesheet representation Applications that want a direct string-parsing API
Douceur inliner HTML containing CSS Rewrites CSS into inline style attributes Email or HTML inlining, not standalone stylesheet parsing

These libraries solve different problems. Select a dependency version compatible with your Go project and check its current CSS feature support and maintenance status before adopting it. No benchmark establishes that one approach is universally faster.

Load a CSS string with a reader-based parser

The standard library provides two convenient in-memory adapters:

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
  • strings.NewReader exposes a string as an io.Reader.
  • bytes.NewBufferString creates a byte buffer initialized from a string.

The documented tdewolff/parse/v2/css API wraps the reader with parse.NewInput, then constructs a parser. The second argument to css.NewParser is the isInline flag: use true for declarations from a style attribute and false for a complete stylesheet.

Complete parser example

package main

import (
    "bytes"
    "fmt"
    "io"

    "github.com/tdewolff/parse/v2"
    "github.com/tdewolff/parse/v2/css"
)

func main() {
    cssText := `body { color: rebeccapurple; margin: 0; }`

    input := parse.NewInput(bytes.NewBufferString(cssText))
    p := css.NewParser(input, false) // false: a full stylesheet

    for {
        grammar, _, data := p.Next()
        if grammar == css.ErrorGrammar {
            break
        }

        // Inspect grammar and data, or call p.Values() when appropriate.
        fmt.Printf("grammar=%v data=%qn", grammar, data)
    }

    if err := p.Err(); err != nil && err != io.EOF {
        panic(err)
    }
}

The loop stops at css.ErrorGrammar. Always inspect p.Err() afterward; stopping the iterator is not, by itself, proof that the input was valid. Depending on the package version and parser state, normal end-of-input may be represented by nil or io.EOF, while malformed input is reported as an error.

Using strings.NewReader instead

If the parser path you are using already accepts an io.Reader, the equivalent adapter is shorter:

cssText := `:root { --accent: #7357ff; }`
reader := strings.NewReader(cssText)
// Pass reader to the reader-oriented API.

Import strings when using this form. Both adapters keep the data in memory and avoid a temporary file.

Free tools Windows power users keep installed

One-click scans. No signup required.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Parse a CSS string directly with Douceur

Douceur documents a direct string interface. This is useful when you want a stylesheet object rather than manually iterating grammar units.

package main

import (
    "fmt"
    "log"

    "github.com/aymerick/douceur/parser"
)

func main() {
    cssText := `h1 { font-size: 2rem; color: navy; }`

    stylesheet, err := parser.Parse(cssText)
    if err != nil {
        log.Fatal(err)
    }

    fmt.Println(stylesheet.String())
}

Handle the returned error before using the stylesheet. The repository example prints the stylesheet with String(); your application can instead inspect or transform the returned representation.

When the direct string API is preferable

  • Choose it when your next operation expects Douceur’s stylesheet representation.
  • Choose the reader-oriented parser when you need grammar-level iteration or an API built around io.Reader.
  • Do not select the inliner merely because it accepts HTML; it performs a different transformation.

Set the inline flag correctly

A CSS declaration inside an HTML element is not the same input context as a complete stylesheet. For example, the value of style="color: red; margin: 0" is declaration text. A document containing body { color: red; } is a stylesheet.

Input isInline value Reason
Contents of a style attribute true Parse declarations intended for an element
Standalone stylesheet or <style> block false Parse stylesheet rules and at-rules

Passing the wrong value can make otherwise valid text appear malformed because the parser applies the wrong grammar context.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Parsing is not fetching, inlining, or browser rendering

Parsing a standalone string

A parser reads the characters you provide and exposes syntax structures. It does not contact the network or discover resources referenced by @import or HTML <link> elements.

Inlining CSS into HTML

Douceur’s inliner handles CSS defined in the HTML document and rewrites matching rules into inline style attributes. Its documented behavior does not fetch external stylesheets. If your HTML references an external file, fetch that file yourself, authenticate as needed, and provide the resulting CSS or HTML to the appropriate operation.

Applying styles in a browser

Go parsing alone does not create a DOM with computed styles. Browser rendering requires a browser engine or a service that captures a page after its resources load. Keep that rendering stage separate from string parsing in your design and error handling.

Build a reusable helper around the reader adapter

If several parts of your program parse CSS, hide the adapter and parser lifecycle behind a function. That keeps callers from accidentally changing the inline mode or forgetting error checks.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
func parseStylesheet(cssText string) error {
    input := parse.NewInput(strings.NewReader(cssText))
    p := css.NewParser(input, false)

    for {
        grammar, _, data := p.Next()
        if grammar == css.ErrorGrammar {
            break
        }
        _ = data
    }

    if err := p.Err(); err != nil && err != io.EOF {
        return err
    }
    return nil
}

For production code, replace the discarded grammar data with the transformation or validation your application needs. Return errors to the caller instead of panicking so an HTTP handler, worker, or command-line tool can choose an appropriate response.

Performance, memory, and reliability considerations

Memory use

Both reader adapters operate on the string in memory. They avoid filesystem latency, but the original string and parser data may coexist while processing. For very large stylesheets, avoid making unnecessary copies and release references when the parse is complete.

Streaming limits

Converting a string to a reader does not make processing truly streaming: the entire CSS source is already present. If CSS arrives from a file or network response, pass that source directly to an API that supports streaming instead of first reading it into a string.

Error handling

Check parse errors at the boundary where untrusted or generated CSS enters your program. Log enough context to identify the source, but avoid logging secrets if CSS was assembled from user-controlled templates or authenticated responses.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Version and feature checks

The examples follow the documented APIs for the named packages. Go modules resolve a specific dependency version, so confirm the exact version’s signatures and supported CSS syntax in your project before copying code. The available documentation does not establish a complete compatibility matrix or benchmark.

Troubleshoot common failures

“Cannot use string as io.Reader”

Cause: The selected function expects an io.Reader, not a string.

Fix: Wrap the value with strings.NewReader(cssText) or bytes.NewBufferString(cssText). Do not write a temporary file just to satisfy the interface.

The parser stops immediately

Cause: The loop reached css.ErrorGrammar, or the parser was given input in the wrong context.

What’s actually slowing this PC down?

Pick the symptom - the matching free tool is one click away.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Fix: Inspect p.Err(), verify that the CSS string is not empty, and set isInline according to whether the input is a declaration list or a full stylesheet.

Valid CSS is reported as invalid

Cause: The parser version may not support the syntax, or the text may be a fragment such as declarations passed as a stylesheet.

Fix: Confirm the dependency version and grammar mode. Test the smallest failing fragment separately from the complete document.

External @import rules are missing

Cause: Parsing does not fetch network resources, and Douceur’s inliner explicitly does not fetch external stylesheets.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Fix: Resolve imports yourself with an HTTP client, apply your authentication and URL policy, then parse the fetched content. Treat remote CSS as untrusted input and enforce timeouts and size limits.

HTML still has class-based styles after using Douceur

Cause: Parsing a stylesheet and inlining it into HTML are separate operations.

Fix: Call the inliner with HTML that contains the CSS definitions it supports. A standalone stylesheet object will not automatically rewrite an unrelated HTML document.

The code compiles locally but not after upgrading

Cause: A dependency API or import path changed between versions.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Fix: Pin and review the module version, read that version’s package documentation, and run your project’s tests before upgrading.

Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Or skip the browser setup

If your reason for loading CSS is to verify the final visual result, a screenshot service can render the page without you managing a browser process. ScreenshotNeo is a website screenshot API and MCP server. It accepts a URL and returns PNG, JPEG, WebP, or PDF output.

Its capture pipeline accepts cookie and consent banners before removing more than 60 known consent platforms, newsletter popups, and chat widgets. Bot checks, CAPTCHAs, blank pages, timeouts, failed loads, and cache hits are not billed; each response identifies the page verdict and billing status with X-Page-Verdict and X-Billed headers. An MCP server provides take_screenshot, get_page_info, and capture_pdf tools to Claude, Cursor, and other MCP clients.

curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp

See the ScreenshotNeo API documentation for request options. You can also use the same endpoint from Go through any HTTP client, or call it from Python:

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
import requests
r = requests.get("https://api.screenshotneo.com/v1/shot", params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"}, timeout=90)
open("shot.webp", "wb").write(r.content)

And from Node.js:

const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);

Every plan includes the full feature set: full-page and element captures, lazy-image loading, device presets, custom viewports, retina scale, dark mode, PDFs with paper and margin controls, custom CSS and JavaScript, selector waits, delays, network-idle waits, request blocking, headers, cookies, user agents, authorization, timezone, geolocation, transparent backgrounds, resizing, configurable caching, signed links, asynchronous jobs with signed webhooks, bulk capture for up to 100 URLs per call, a usage API, and an OpenAPI specification. Parameter names used by other screenshot APIs also work.

The Free plan includes 1,000 screenshots per month with no card. Paid plans start at $5 for 3,000 screenshots; yearly billing gives two months free. Create a free ScreenshotNeo account to try it without adding a card.

Frequently asked questions

Can I pass a []byte value instead of a string?

Yes. Use a reader backed by the bytes, such as bytes.NewReader(cssBytes), when the selected parser accepts io.Reader. The parser still receives the same CSS text; only the source adapter changes.

Should I parse CSS once and reuse the result?

Reuse a parsed representation when your dependency exposes one and the stylesheet is unchanged. Reparse when the source, variables, or transformation context changes, and protect shared state if multiple goroutines access mutable structures.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Does html/template parse a stylesheet string?

No. Its CSS-related behavior concerns template escaping and output safety, not a general-purpose stylesheet parser interface. Use a CSS parser for grammar processing and an HTML inliner or browser engine for their respective tasks.

Frequently Asked Questions

What is the simplest way to turn a Go CSS string into an io.Reader?

Use strings.NewReader(cssText); bytes.NewBufferString(cssText) is an equivalent in-memory option.

How do I know whether to set isInline to true?

Set it to true only when parsing declarations from a style attribute. Use false for a complete stylesheet.

Will a Go CSS parser download linked stylesheets?

No. Parsing is local. Fetch external stylesheets yourself, or use a separate workflow designed for HTML inlining or browser rendering.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.