diff --git a/LICENSE b/LICENSE
new file mode 100644
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,30 @@
+Copyright David Ackerman (c) 2016
+
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+    * Redistributions of source code must retain the above copyright
+      notice, this list of conditions and the following disclaimer.
+
+    * Redistributions in binary form must reproduce the above
+      copyright notice, this list of conditions and the following
+      disclaimer in the documentation and/or other materials provided
+      with the distribution.
+
+    * Neither the name of Author name here nor the names of other
+      contributors may be used to endorse or promote products derived
+      from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/Setup.hs b/Setup.hs
new file mode 100644
--- /dev/null
+++ b/Setup.hs
@@ -0,0 +1,2 @@
+import Distribution.Simple
+main = defaultMain
diff --git a/reflex-jsx.cabal b/reflex-jsx.cabal
new file mode 100644
--- /dev/null
+++ b/reflex-jsx.cabal
@@ -0,0 +1,50 @@
+name:                reflex-jsx
+version:             0.1.0.0
+stability:           Experimental
+synopsis:            Use jsx-like syntax in Reflex
+description:         `reflex-jsx` is a library for writing jsx-like syntax in
+                     reflex code. This can be useful for situations where you
+                     have a large block of HTML with some styles, and it would
+                     be easier to read as actual HTML than various reflex
+                     functions.
+
+                     It's implemented as a "QuasiQuoter", so you just import the
+                     `jsx` function from ReflexJsx and generate the equivalent
+                     functions that would run inside of a "MonadWidget t m".
+
+                     Not only can you generate a block of static HTML/CSS, but you
+                     can also splice in your own nodes that implement
+                     "MonadWidget t m", string attribute values, or even entire
+                     dynamic attribute maps.
+
+homepage:            https://github.com/dackerman/reflex-jsx
+bug-reports:         https://github.com/dackerman/reflex-jsx/issues
+license:             BSD3
+license-file:        LICENSE
+author:              David Ackerman
+maintainer:          david.w.ackerman@gmail.com
+copyright:           2016 David Ackerman
+category:            FRP, Web, GUI, JSX, Language
+build-type:          Simple
+cabal-version:       >=1.10
+
+library
+  hs-source-dirs:      src
+  exposed-modules:     ReflexJsx
+                     , ReflexJsx.Parser
+                     , ReflexJsx.QQ
+  ghc-options:         -Wall -fwarn-tabs -funbox-strict-fields -ferror-spans
+  build-depends:       base >= 4.8 && < 5
+                     , parsec >= 3.1 && < 3.2
+                     , reflex
+                     , reflex-dom
+                     , template-haskell
+                     , haskell-src-meta
+                     , containers
+                     , text
+  default-language:    Haskell2010
+
+
+source-repository head
+  type:     git
+  location: https://github.com/dackerman/reflex-jsx
diff --git a/src/ReflexJsx.hs b/src/ReflexJsx.hs
new file mode 100644
--- /dev/null
+++ b/src/ReflexJsx.hs
@@ -0,0 +1,7 @@
+module ReflexJsx
+       ( module ReflexJsx.Parser
+       , module ReflexJsx.QQ
+       ) where
+
+import ReflexJsx.Parser
+import ReflexJsx.QQ
diff --git a/src/ReflexJsx/Parser.hs b/src/ReflexJsx/Parser.hs
new file mode 100644
--- /dev/null
+++ b/src/ReflexJsx/Parser.hs
@@ -0,0 +1,147 @@
+{-| The Parser for the reflex-jsx language
+
+    Given a "String", @parseJsx@ outputs the AST for the language. Note that at
+    this point, we capture spliced expressions from the meta-language as
+    Strings, and parse them during the quasiquoting phase in @ReflexJsx.QQ@
+-}
+module ReflexJsx.Parser
+       ( parseJsx
+       , Node(..)
+       , Attrs(..)
+       , AttrValue(..)
+       ) where
+
+import Text.Parsec (runParser, Parsec, try, eof, many, many1, between)
+import Text.Parsec.Char (char, letter, noneOf, string, alphaNum, spaces)
+
+import Control.Applicative ((<|>))
+
+
+data AttrValue = TextVal String
+               | ExprVal String
+
+
+data Attrs = SplicedAttrs String
+           | StaticAttrs [(String, AttrValue)]
+
+
+data Node = Node String Attrs [Node]
+          | Text String
+          | SplicedNode String
+
+
+parseJsx :: Monad m => String -> m Node
+parseJsx s =
+  case runParser p () "" s of
+    Left err -> fail $ show err
+    Right e -> return e
+  where p = do
+          spaces
+          node <- jsxElement
+          spaces
+          eof
+          return node
+
+
+jsxElement :: Parsec String u Node
+jsxElement = do
+  try jsxSelfClosingElement <|> jsxNormalElement
+
+
+jsxSelfClosingElement :: Parsec String u Node
+jsxSelfClosingElement = do
+  _ <- char '<'
+  name <- jsxElementName
+  attrs <- jsxNodeAttrs
+  _ <- string "/>"
+  return (Node name attrs [])
+
+
+jsxNormalElement :: Parsec String u Node
+jsxNormalElement = do
+  (name, attrs) <- jsxOpeningElement
+  children <- many jsxChild
+  jsxClosingElement name
+  return (Node name attrs children)
+
+
+jsxOpeningElement :: Parsec String u (String, Attrs)
+jsxOpeningElement = do
+  _ <- char '<'
+  name <- jsxElementName
+  attrs <- jsxNodeAttrs
+  _ <- char '>'
+  return (name, attrs)
+
+
+jsxNodeAttrs :: Parsec String u Attrs
+jsxNodeAttrs = do
+  try jsxSplicedAttrMap <|> (StaticAttrs <$> many jsxNodeAttr)
+
+
+jsxSplicedAttrMap :: Parsec String u Attrs
+jsxSplicedAttrMap = do
+  name <- between (string "{...") (string "}") $ many (noneOf "}")
+  return $ SplicedAttrs name
+
+
+jsxNodeAttr :: Parsec String u (String, AttrValue)
+jsxNodeAttr = do
+  key <- jsxAttributeName
+  spaces
+  _ <- char '='
+  spaces
+  value <- jsxQuotedValue <|> jsxSplicedValue
+  spaces
+  return (key, value)
+
+
+jsxAttributeName :: Parsec String u String
+jsxAttributeName = do
+  many $ letter <|> char '-'
+
+
+jsxQuotedValue :: Parsec String u AttrValue
+jsxQuotedValue = do
+  contents <- between (char '"') (char '"') $ many (noneOf "\"")
+  return $ TextVal contents
+
+
+jsxSplicedValue :: Parsec String u AttrValue
+jsxSplicedValue = do
+  name <- between (char '{') (char '}') $ many (noneOf "}")
+  return $ ExprVal name
+
+
+jsxClosingElement :: String -> Parsec String u ()
+jsxClosingElement ele = do
+  _ <- string "</" *> string ele *> char '>'
+  return ()
+
+
+jsxChild :: Parsec String u Node
+jsxChild = do
+  try jsxText <|> try jsxSplicedNode <|> try jsxElement
+
+
+jsxText :: Parsec String u Node
+jsxText = do
+  contents <- many1 $ noneOf "{<>}"
+  return $ Text contents
+
+
+jsxSplicedNode :: Parsec String u Node
+jsxSplicedNode = do
+  exprString <- between (char '{') (char '}') $ many (noneOf "}")
+  return $ SplicedNode exprString
+
+
+jsxElementName :: Parsec String u String
+jsxElementName = jsxIdentifier
+
+
+jsxIdentifier :: Parsec String u String
+jsxIdentifier = do
+  name <- many1 alphaNum
+  spaces
+  return name
diff --git a/src/ReflexJsx/QQ.hs b/src/ReflexJsx/QQ.hs
new file mode 100644
--- /dev/null
+++ b/src/ReflexJsx/QQ.hs
@@ -0,0 +1,72 @@
+{-| The Quasiquoter for the reflex-jsx language
+
+    The only import you need is "jsx", which is the quasiquoter. See the README
+    for more information.
+-}
+
+{-# LANGUAGE TemplateHaskell #-}
+
+module ReflexJsx.QQ
+       ( jsx
+       ) where
+
+import qualified Language.Haskell.TH as TH
+import Language.Haskell.TH.Quote
+import Language.Haskell.Meta (parseExp)
+
+import qualified Data.List as List
+import qualified Data.Map as Map
+
+import qualified Reflex.Dom as Dom
+
+import ReflexJsx.Parser
+
+import Prelude hiding (exp)
+
+{-| Quasiquoter for jsx-like expressions
+
+    Used like "[jsx| <div /> |]"
+-}
+jsx :: QuasiQuoter
+jsx = QuasiQuoter
+  { quoteExp = quoteJsxExpression
+  , quotePat = undefined
+  , quoteDec = undefined
+  , quoteType = undefined
+  }
+
+
+quoteJsxExpression :: String -> TH.ExpQ
+quoteJsxExpression str = do
+  exp <- parseJsx str
+  outputWidgetCode exp
+
+
+outputWidgetCode :: Node -> TH.ExpQ
+outputWidgetCode node =
+  case node of
+    Node tag attrs children -> outputNode tag attrs children
+    Text content -> [| Dom.text content |]
+    SplicedNode varName -> do
+      let Right exp = parseExp varName
+      return exp
+
+
+outputNode :: String -> Attrs -> [Node] -> TH.ExpQ
+outputNode tag attrs children =
+  let renderedChildren = TH.listE $ List.map outputWidgetCode children
+  in case attrs of
+    StaticAttrs staticAttrs ->
+      let stringAttrs = TH.listE $ List.map toStringAttr staticAttrs
+      in [| Dom.elAttr tag (Map.fromList $(stringAttrs)) $ sequence_ $(renderedChildren) |]
+    SplicedAttrs attrExpr -> do
+      let Right exp = parseExp attrExpr
+      [| Dom.elDynAttr tag $(return exp) $ sequence_ $(renderedChildren) |]
+
+
+toStringAttr :: (String, AttrValue) -> TH.ExpQ
+toStringAttr (key, value) = case value of
+  TextVal content -> [| (key, content) |]
+  ExprVal exprString -> do
+    let Right exp = parseExp exprString
+    [| (key, $(return exp)) |]
