diff --git a/assets/texture_atlas.png b/assets/texture_atlas.png
new file mode 100644
index 0000000000000000000000000000000000000000..939cb2835adae278f6ac31d8ea82e21ba3f9139a
Binary files /dev/null and b/assets/texture_atlas.png differ
diff --git a/bevy_kayak_ui/src/render/mod.rs b/bevy_kayak_ui/src/render/mod.rs
index 69215ae78fc23ad9b177d3eb944bd23b569ac871..efdfcd5ce898e925e8b38f42d966806f79240d9b 100644
--- a/bevy_kayak_ui/src/render/mod.rs
+++ b/bevy_kayak_ui/src/render/mod.rs
@@ -16,6 +16,7 @@ use kayak_font::KayakFont;
 pub mod font;
 pub mod image;
 mod nine_patch;
+mod texture_atlas;
 mod quad;
 
 pub struct BevyKayakUIExtractPlugin;
@@ -79,6 +80,11 @@ pub fn extract(
                     nine_patch::extract_nine_patch(&render_primitive, &image_manager, &images, dpi);
                 extracted_quads.extend(nine_patch_quads);
             }
+            RenderPrimitive::TextureAtlas { .. } => {
+                let texture_atlas_quads =
+                    texture_atlas::extract_texture_atlas(&render_primitive, &image_manager, &images, dpi);
+                extracted_quads.extend(texture_atlas_quads);
+            }
             RenderPrimitive::Clip { layout } => {
                 extracted_quads.push(ExtractQuadBundle {
                     extracted_quad: ExtractedQuad {
diff --git a/bevy_kayak_ui/src/render/texture_atlas/extract.rs b/bevy_kayak_ui/src/render/texture_atlas/extract.rs
new file mode 100644
index 0000000000000000000000000000000000000000..efe552e9d5dc4848df4f4de3b0e5ef4ba71c792d
--- /dev/null
+++ b/bevy_kayak_ui/src/render/texture_atlas/extract.rs
@@ -0,0 +1,80 @@
+use crate::ImageManager;
+use bevy::{
+    math::Vec2,
+    prelude::{Assets, Res},
+    render::{color::Color, texture::Image},
+    sprite::Rect,
+};
+use bevy_kayak_renderer::{
+    render::unified::pipeline::{ExtractQuadBundle, ExtractedQuad, UIQuadType},
+    Corner,
+};
+use kayak_core::render_primitive::RenderPrimitive;
+
+pub fn extract_texture_atlas(
+    render_primitive: &RenderPrimitive,
+    image_manager: &Res<ImageManager>,
+    images: &Res<Assets<Image>>,
+    dpi: f32,
+) -> Vec<ExtractQuadBundle> {
+    let mut extracted_quads = Vec::new();
+
+    let (size, position, layout, handle) = match render_primitive {
+        RenderPrimitive::TextureAtlas {
+        size,
+        position,
+            layout,
+            handle,
+        } => (size, position, layout, handle),
+        _ => panic!(""),
+    };
+
+    let image_handle = image_manager
+        .get_handle(handle)
+        .and_then(|a| Some(a.clone_weak()));
+
+    let image = images.get(image_handle.as_ref().unwrap());
+
+    if image.is_none() {
+        return vec![];
+    }
+
+    let image_size = image
+        .and_then(|i| {
+            Some(Vec2::new(
+                i.texture_descriptor.size.width as f32,
+                i.texture_descriptor.size.height as f32,
+            ))
+        })
+        .unwrap()
+        * dpi;
+
+    let quad = ExtractQuadBundle {
+        extracted_quad: ExtractedQuad {
+            rect: Rect {
+                min: Vec2::new(layout.posx, layout.posy),
+                max: Vec2::new(layout.posx + layout.width, layout.posy + layout.height),
+            },
+            uv_min: Some(Vec2::new(
+                position.0 / image_size.x,
+                1.0 - ((position.1 + size.1) / image_size.y)
+            )),
+            uv_max: Some(Vec2::new(
+                (position.0 + size.0) / image_size.x,
+                1.0 - (position.1 / image_size.y),
+            )),
+        color: Color::WHITE,
+        vertex_index: 0,
+        char_id: 0,
+        z_index: layout.z_index,
+        font_handle: None,
+        quad_type: UIQuadType::Image,
+        type_index: 0,
+        border_radius: Corner::default(),
+        image: image_handle,
+        },
+    };
+    extracted_quads.push(quad);
+
+    extracted_quads
+}
diff --git a/bevy_kayak_ui/src/render/texture_atlas/mod.rs b/bevy_kayak_ui/src/render/texture_atlas/mod.rs
new file mode 100644
index 0000000000000000000000000000000000000000..bd5825ad3486b72cd8bdbdd548773246edcc49cd
--- /dev/null
+++ b/bevy_kayak_ui/src/render/texture_atlas/mod.rs
@@ -0,0 +1,2 @@
+mod extract;
+pub use extract::extract_texture_atlas;
diff --git a/examples/texture_atlas.rs b/examples/texture_atlas.rs
new file mode 100644
index 0000000000000000000000000000000000000000..a5f4de0812de9ae2f81f67c7811c7994eb72de50
--- /dev/null
+++ b/examples/texture_atlas.rs
@@ -0,0 +1,84 @@
+use bevy::{
+    prelude::{App as BevyApp, AssetServer, Commands, Handle, Res, ResMut},
+    window::WindowDescriptor,
+    DefaultPlugins,
+};
+use kayak_core::styles::PositionType;
+use kayak_ui::bevy::{BevyContext, BevyKayakUIPlugin, ImageManager, UICameraBundle};
+use kayak_ui::core::{
+    render,
+    styles::{Style, StyleProp, Units},
+    Index,
+};
+use kayak_ui::widgets::{App, TextureAtlas};
+
+fn startup(
+    mut commands: Commands,
+    asset_server: Res<AssetServer>,
+    mut image_manager: ResMut<ImageManager>,
+) {
+    commands.spawn_bundle(UICameraBundle::new());
+
+    let image_handle: Handle<bevy::render::texture::Image> = asset_server.load("texture_atlas.png");
+    let ui_image_handle = image_manager.get(&image_handle);
+
+    //texture_atlas.png uses 16 pixel sprites and is 272x128 pixels
+    let tile_size = 16;
+    let columns = 272 / tile_size;
+    let rows = 128 / tile_size;
+    let atlas = bevy::sprite::TextureAtlas::from_grid(image_handle, bevy::prelude::Vec2::splat(tile_size as f32), columns, rows);
+
+    //The sign in the top right of the image would be index 16
+    let sign_index = 16;
+    //The flower is in the 6(-1) row and 15 collumn
+    let flower_index = columns * 5 + 15;
+
+    let context = BevyContext::new(|context| {
+        let atlas_styles = Style {
+            position_type: StyleProp::Value(PositionType::ParentDirected),
+            width: StyleProp::Value(Units::Pixels(200.0)),
+            height: StyleProp::Value(Units::Pixels(200.0)),
+            ..Style::default()
+        };
+
+        let rect = atlas.textures[sign_index];
+        let sign_position = rect.min;
+        let sign_size = rect.max - rect.min;
+
+        let rect = atlas.textures[flower_index];
+        let flower_position = rect.min;
+        let flower_size = rect.max - rect.min;
+
+        render! {
+            <App>
+                <TextureAtlas styles={Some(atlas_styles)} 
+                handle={ui_image_handle} 
+                position={(sign_position.x, sign_position.y)} 
+                tile_size={(sign_size.x, sign_size.y)} 
+                 />
+                <TextureAtlas styles={Some(atlas_styles)} 
+                handle={ui_image_handle} 
+                position={(flower_position.x, flower_position.y)} 
+                tile_size={(flower_size.x, flower_size.y)} 
+                 />
+            </App>
+        }
+    });
+
+    commands.insert_resource(context);
+}
+
+fn main() {
+    BevyApp::new()
+        .insert_resource(WindowDescriptor {
+            width: 1270.0,
+            height: 720.0,
+            title: String::from("UI Example"),
+            ..Default::default()
+        })
+        .add_plugins(DefaultPlugins)
+        .add_plugin(BevyKayakUIPlugin)
+        .add_startup_system(startup)
+        .run();
+}
+
diff --git a/kayak_core/src/render_command.rs b/kayak_core/src/render_command.rs
index 6a7b40880451809cb1fe4de1aa6fc7da0a97774d..13f9cbaee03ea17dfc83f3686e6a774014889f9a 100644
--- a/kayak_core/src/render_command.rs
+++ b/kayak_core/src/render_command.rs
@@ -13,6 +13,11 @@ pub enum RenderCommand {
     Image {
         handle: u16,
     },
+    TextureAtlas {
+        position: (f32, f32),
+        size: (f32, f32),
+        handle: u16,
+    },
     NinePatch {
         border: Edge<f32>,
         handle: u16,
diff --git a/kayak_core/src/render_primitive.rs b/kayak_core/src/render_primitive.rs
index 127c3060bde68d3b211a13a41b65ac9ae052d283..11a589ce77a2018e485c9c46bbef2004398a5e83 100644
--- a/kayak_core/src/render_primitive.rs
+++ b/kayak_core/src/render_primitive.rs
@@ -32,6 +32,12 @@ pub enum RenderPrimitive {
         layout: Rect,
         handle: u16,
     },
+    TextureAtlas {
+        size: (f32, f32),
+        position: (f32, f32),
+        layout: Rect,
+        handle: u16,
+    },
     NinePatch {
         border: Edge<f32>,
         layout: Rect,
@@ -47,6 +53,7 @@ impl RenderPrimitive {
             RenderPrimitive::Text { layout, .. } => *layout = new_layout,
             RenderPrimitive::Image { layout, .. } => *layout = new_layout,
             RenderPrimitive::NinePatch { layout, .. } => *layout = new_layout,
+            RenderPrimitive::TextureAtlas { layout, .. } => *layout = new_layout,
             _ => (),
         }
     }
@@ -98,6 +105,12 @@ impl From<&Style> for RenderPrimitive {
                 layout: Rect::default(),
                 handle,
             },
+            RenderCommand::TextureAtlas { handle, size, position,  } => Self::TextureAtlas {
+                handle,
+                layout: Rect::default(),
+                size,
+                position,
+            },
             RenderCommand::NinePatch { handle, border } => Self::NinePatch {
                 border,
                 layout: Rect::default(),
diff --git a/src/widgets/mod.rs b/src/widgets/mod.rs
index a8c19d61c4284cae1dffa1abb6252ad3700bce06..e4915a51a1ae10c1ba8ecbae27892dd1695983df 100644
--- a/src/widgets/mod.rs
+++ b/src/widgets/mod.rs
@@ -8,6 +8,7 @@ mod if_element;
 mod image;
 mod inspector;
 mod nine_patch;
+mod texture_atlas;
 mod on_change;
 mod scroll;
 mod spin_box;
@@ -26,6 +27,7 @@ pub use if_element::*;
 pub use image::*;
 pub use inspector::*;
 pub use nine_patch::*;
+pub use texture_atlas::*;
 pub use on_change::*;
 pub use scroll::*;
 pub use spin_box::*;
diff --git a/src/widgets/texture_atlas.rs b/src/widgets/texture_atlas.rs
new file mode 100644
index 0000000000000000000000000000000000000000..07ef35c344dfc70d64ee8108ddb8e252294aa35f
--- /dev/null
+++ b/src/widgets/texture_atlas.rs
@@ -0,0 +1,62 @@
+use kayak_core::OnLayout;
+
+use crate::core::{
+    render_command::RenderCommand,
+    rsx,
+    styles::{Style, StyleProp},
+    widget, Children, OnEvent, WidgetProps,
+};
+
+/// Props used by the [`NinePatch`] widget
+#[derive(WidgetProps, Default, Debug, PartialEq, Clone)]
+pub struct TextureAtlasProps {
+    /// The handle to image
+    pub handle: u16,
+    /// The position of the tile (in pixels)
+    pub position: (f32, f32),
+    /// The size of the tile (in pixels)
+    pub tile_size: (f32, f32),
+    #[prop_field(Styles)]
+    pub styles: Option<Style>,
+    #[prop_field(Children)]
+    pub children: Option<Children>,
+    #[prop_field(OnEvent)]
+    pub on_event: Option<OnEvent>,
+    #[prop_field(OnLayout)]
+    pub on_layout: Option<OnLayout>,
+    #[prop_field(Focusable)]
+    pub focusable: Option<bool>,
+}
+
+#[widget]
+/// A widget that renders a texture atlas
+/// Allows for the use of a partial square of an image such as in a sprite sheet
+/// 
+/// # Props
+///
+/// __Type:__ [`TextureAtlasProps`]
+///
+/// | Common Prop | Accepted |
+/// | :---------: | :------: |
+/// | `children`  | ✅        |
+/// | `styles`    | ✅        |
+/// | `on_event`  | ✅        |
+/// | `on_layout` | ✅        |
+/// | `focusable` | ✅        |
+///
+pub fn TextureAtlas(props: TextureAtlasProps) {
+    props.styles = Some(Style {
+        render_command: StyleProp::Value(RenderCommand::TextureAtlas {
+            position: props.position,
+            size: props.tile_size,
+            handle: props.handle,
+        }),
+        ..props.styles.clone().unwrap_or_default()
+    });
+
+    rsx! {
+        <>
+            {children}
+        </>
+    }
+}