Given HTML, Juice will inline your CSS properties into the style attribute.
Juice has a number of functions based on whether you want to process a file, HTML string, or a cheerio document, and whether you want Juice to automatically get remote stylesheets, scripts and image dataURIs to inline.
To inline HTML without getting remote resources, using default options:
import juice from 'juice';
const result = juice("<style>div{color:red;}</style><div/>");result will be:
<div style="color: red;"></div>Try out the web client version
- HTML emails. For CSS support in email clients see Can I Email.
- Embedding HTML in 3rd-party websites.
Juice is exposed as a standard module, and from CLI with a smaller set of options.
Juice handles modern CSS out of the box: nested rules (CSS Nesting Module Level 1, .card { &:hover { ... } }) are flattened automatically before inlining, @container and @layer at-rules pass through verbatim, and specificity for :is() / :where() / :has() / :not() is computed per the CSS Selectors Level 4 spec.
All Juice methods take an options object that can contain any of these properties, though not every method uses all of these:
| Option | Default value | Description |
|---|---|---|
applyAttributesTableElements |
true |
Create attributes for styles in juice.styleToAttribute on elements set in juice.tableElements. |
applyHeightAttributes |
true |
Use any CSS pixel heights to create height attributes on elements set in juice.heightElements. |
applyStyleTags |
true |
Inline styles in <style> tags. |
applyWidthAttributes |
true |
Use any CSS pixel widths to create width attributes on elements set in juice.widthElements. |
decodeStyleAttributes |
false |
Decode the value of style attributes. |
extraCss |
"" |
Extra CSS to apply to the file. |
inlineDuplicateProperties |
false |
When true, declarations with identical CSS properties will all be inlined, instead of only the one with the highest specificity. Useful for progressive enhancement. |
insertPreservedExtraCss |
true |
Whether to insert into the document any preserved @media or @font-face content from extraCss when using preserveMediaQueries, preserveFontFaces or preserveKeyFrames. When set to true, order of preference to append the <style> element is into head, then body, then at the end of the document. When a string the value is treated as a CSS/jQuery/cheerio selector, and when found, the <style> tag will be appended to the end of the first match. |
inlinePseudoElements |
false |
Insert pseudo elements (::before and ::after) as <span> into the DOM. Note: Inserting pseudo elements will modify the DOM and may conflict with CSS selectors elsewhere on the page (e.g., :last-child). |
preserveFontFaces |
true |
Preserve all @font-face within <style> tags as a refinement when removeStyleTags is true. Other styles are removed. |
preserveImportant |
false |
Preserve !important in CSS values. |
preserveMediaQueries |
true |
Preserve all media queries (and contained styles) within <style> tags as a refinement when removeStyleTags is true. Other styles are removed. |
preserveKeyFrames |
true |
Preserve all key frames within <style> tags as a refinement when removeStyleTags is true. Other styles are removed. |
preservePseudos |
true |
Preserve all rules containing pseudo selectors defined in ignoredPseudos within <style> tags as a refinement when removeStyleTags is true. Other styles are removed. |
preservedSelectors |
[] |
Array of strings that represent CSS selectors to preserve inside <style> tags when removeStyleTags or removeInlinedSelectors are true. Matches substrings. |
removeInlinedSelectors |
false |
Remove CSS rules from <style> tags after (possibly) inlining them. Other rules are preserved. Works only if removeStyleTags is false. |
removeStyleTags |
true |
Remove the original <style> tags after (possibly) inlining their CSS content. Overrides removeInlinedSelectors. |
resolveCSSVariables |
true |
Resolve CSS variables. |
webResources |
{} |
An options object that will be passed to web-resource-inliner for Juice functions that will get remote resources (juiceResources and juiceFile). |
xmlMode |
false |
Output XML/XHTML with all tags closed. Note that the input must also be valid XML/XHTML or you will get undesirable results. |
Returns a string containing the HTML with inlined CSS.
Does not fetch remote resources.
html- HTML string, accepts complete documents as well as fragmentsoptions- optional, see Options above
Callback returns a string containing the HTML with inlined CSS.
Fetches remote resources.
html- (string) HTMLoptions- see Options abovecallback(err, html)err-Errorobject ornullhtml- (string) HTML with inlined CSS
Callback returns a string containing the HTML with inlined CSS.
Fetches remote resources.
filePath- path to the HTML file to be Juicedoptions- see Options abovecallback(err, html)err-Errorobject ornullhtml- (string) HTML with inlined CSS
This takes a cheerio instance and performs inlining in-place. Returns the same cheerio instance.
Does not fetch remote resources.
$- acheerioinstance, be sure to use the samecheerioversion that Juice usesoptions- (object) optional, see Options above
This takes HTML and CSS and returns new HTML with the provided CSS inlined.
It does not look at <style> or <link rel="stylesheet"> elements at all.
html- HTML stringcss- CSS stringoptions- (object) optional, see Options above
Given a cheerio instance and CSS, this modifies the cheerio instance so that the provided CSS is inlined. It does not look at <style> or <link rel="stylesheet"> elements at all.
$- acheerioinstance, be sure to use the samecheerioversion that juice usescss- CSS stringoptions- (object) optional, see Options above
Type: Object
Default:
{
EJS: { start: '<%', end: '%>' },
HBS: { start: '{{', end: '}}' }
}An object where each value has a start and end to specify fenced code blocks that should be ignored during parsing and inlining.
For example, Handlebars (hbs) templates are juice.codeBlocks.HBS = {start: '{{', end: '}}'}. codeBlocks can fix problems where otherwise juice might interpret code like <= as HTML, when it is meant to be template language code.
Note that codeBlocks is a dictionary which can contain many different code blocks, so don't do juice.codeBlocks = {...} do juice.codeBlocks.myBlock = {...}
Type: Array
Default: ['hover', 'active', 'focus', 'visited', 'link']
Array of ignored pseudo-selectors such as 'hover' and 'active'.
Type: Array
Default: ['TABLE', 'TD', 'TH', 'IMG']
Array of HTML elements that can receive width attributes.
Type: Array
Default: ['TABLE', 'TD', 'TH', 'IMG']
Array of HTML elements that can receive height attributes.
Type: Object
Default:
{
'background-color': 'bgcolor',
'background-image': 'background',
'text-align': 'align',
'vertical-align': 'valign'
}Object of style property names (key) to their respective attribute names (value).
Type: Array
Default: ['TABLE', 'TH', 'TR', 'TD', 'CAPTION', 'COLGROUP', 'COL', 'THEAD', 'TBODY', 'TFOOT']
Array of table HTML elements that can receive attributes defined in juice.styleToAttribute.
Type: Array
Default: [ 'HEAD', 'TITLE', 'BASE', 'LINK', 'STYLE', 'META', 'SCRIPT', 'NOSCRIPT' ]
Array of elements that will not have styles inlined because they are not intended to render.
Type: Array
Default: []
Array of CSS properties that won't be inlined.
Add data-embed to any <style> tag to prevent Juice from inlining its CSS and removing it.
Can be used to embed email client support hacks that rely on CSS selectors into your email templates:
<style data-embed>
u + .body .your-class-name {
/* CSS here targets Gmail */
}
</style>The data-embed attribute will be removed from the output HTML, but no inlining or style tag removal will take place:
<style>
u + .body .your-class-name {
/* CSS here targets Gmail */
}
</style>Override the inlineDuplicateProperties option for a single element by adding data-juice-duplicates to it. The attribute presence (or data-juice-duplicates="true") enables duplicate declarations for that element; data-juice-duplicates="false" disables them:
<!-- keeps both background-color declarations, even if the option is off -->
<div class="header" data-juice-duplicates></div>
<!-- collapses to the highest-specificity declaration, even if the option is on -->
<div class="header" data-juice-duplicates="false"></div>The data-juice-duplicates attribute is removed from the output HTML.
Override the preserveImportant option for a single element by adding data-juice-important to it. The attribute presence (or data-juice-important="true") preserves !important in that element's inlined declarations; data-juice-important="false" strips it:
<!-- keeps !important in the inlined style, even if the option is off -->
<div class="cta" data-juice-important></div>
<!-- strips !important from the inlined style, even if the option is on -->
<div class="cta" data-juice-important="false"></div>The data-juice-important attribute is removed from the output HTML.
You can use special CSS comments to prevent Juice from inlining entire CSS files, rules, or even just declarations.
Add a /* juice ignore */ comment on the first line in your CSS:
<style>
/* juice ignore */
body { color: red; }
.test { color: blue; }
</style>
<body>
<div class="test">Hello World</div>
</body>The entire CSS will be ignored (as in, not inlined).
With removeStyleTags: true, the whole <style> tag will be preserved.
Add /* juice ignore next */ before any CSS rule to skip inlining that rule:
body { color: black; }
/* juice ignore next */
h1 {
color: blue;
}
p { color: green; }The h1 rule will not be inlined, but will be preserved when removeStyleTags: true. The body and p rules will be inlined normally.
Add /* juice ignore next */ inside a CSS rule to skip inlining the next declaration:
.test {
color: red;
/* juice ignore next */
font-weight: bold;
font-size: 14px;
}The color and font-size will be inlined, but font-weight will not. The rule with the ignored declaration will be preserved in a <style> tag when removeStyleTags: true.
Use start/end comment pairs to ignore multiple rules:
h1 {
color: black;
}
/* juice start ignore */
h2 {
color: pink;
}
h3 {
color: lightcoral;
}
/* juice end ignore */
h4 {
color: green;
}The h2 and h3 rules will not be inlined but will be preserved in a <style> tag when removeStyleTags: true. The h1 and h4 rules will be inlined normally.
To use Juice from CLI, run juice [options] input.html output.html
For a listing of all available options, just type juice -h.
Note that if you want to just type
juicefrom the command line, you shouldnpm install juice -gso it is globally available.
The CLI should have all the above options with the names changed from camel case to hyphen-delimited, so for example extraCss becomes extra-css and webResources.scripts becomes web-resources-scripts.
These are additional options not included in the standard juice options listed above:
--css [filepath]will load and inject CSS intoextraCss.--options-file [filepath]will load and inject options from a JSON file. Options from the CLI will be given priority over options in the file when there is a conflict.codeBlocksis optionally supported in the options file if you include it. This will allow you to support different template languages in a build process.
Juice's default entry imports Node-only modules (fs, path), so it cannot run as-is in the browser. For browser builds, import juice/client instead, which exposes juiceDocument, inlineDocument, and inlineContent (but not juiceFile, juiceResources, or inlineExternal).
Juice is ESM-only as of v12. Any modern bundler — Vite, webpack 5, esbuild, Rollup, Parcel 2+ — will pick up juice/client correctly via the "browser" field in package.json. Browserify is not supported on v12+ (it can't parse ESM). Note that automated tests are not running in the browser yet.
MIT Licensed, see LICENSE.md.
- Uses cheerio for the underlying DOM representation.
- Uses mensch to parse out CSS and Slick to tokenize them.
- Icon by UnheardSounds
